mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Merge branch 'master' into control-exception-on-sigterm
This commit is contained in:
commit
58d9824920
37 changed files with 616 additions and 228 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1,2 +1,3 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text eol=lf
|
||||
*.png binary
|
||||
|
|
|
|||
26
.travis.yml
26
.travis.yml
|
|
@ -1,11 +1,9 @@
|
|||
dist: trusty
|
||||
sudo: false
|
||||
group: beta
|
||||
dist: xenial
|
||||
language: ruby
|
||||
cache: bundler
|
||||
|
||||
before_install:
|
||||
# rubygems 2.7.8 and greater include bundler
|
||||
# rubygems 2.7.8 and greater include bundler, leave 2.6.0 untouched
|
||||
- |
|
||||
rv="$(ruby -e 'STDOUT.write RUBY_VERSION')";
|
||||
if [ "$rv" \< "2.3" ]; then gem update --system 2.7.8 --no-document
|
||||
|
|
@ -13,13 +11,19 @@ before_install:
|
|||
fi
|
||||
- ruby -v && gem --version && bundle version
|
||||
|
||||
before_script:
|
||||
- bundle exec rake compile
|
||||
|
||||
script:
|
||||
- bundle exec rake
|
||||
|
||||
rvm:
|
||||
- 2.2.10
|
||||
- 2.3.8
|
||||
- 2.4.5
|
||||
- 2.5.3
|
||||
- 2.6
|
||||
- ruby-head
|
||||
- jruby-9.2.0.0
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
|
@ -30,15 +34,21 @@ matrix:
|
|||
os: osx
|
||||
- rvm: 2.5.3
|
||||
os: osx
|
||||
- rvm: jruby-9.2.5.0
|
||||
dist: trusty
|
||||
sudo: false
|
||||
- rvm: jruby-head
|
||||
- rvm: rbx-3
|
||||
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
allow_failures:
|
||||
- rvm: 2.6
|
||||
- rvm: ruby-head
|
||||
- rvm: ruby-head
|
||||
env: RUBYOPT="--jit"
|
||||
- rvm: jruby-head
|
||||
- rvm: rbx-3
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
|
|
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -180,7 +182,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);
|
||||
|
|
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -187,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"
|
||||
|
|
|
|||
|
|
@ -147,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
|
||||
|
|
@ -172,8 +175,7 @@ module Puma
|
|||
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
|
||||
|
||||
|
|
@ -211,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"
|
||||
|
|
@ -221,8 +223,7 @@ module Puma
|
|||
unless chunk
|
||||
@body.close
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
raise EOFError
|
||||
end
|
||||
|
||||
|
|
@ -231,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
|
||||
|
|
@ -255,8 +258,7 @@ module Puma
|
|||
unless cl
|
||||
@buffer = body.empty? ? nil : body
|
||||
@body = EmptyBody
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
@ -265,8 +267,7 @@ module Puma
|
|||
if remain <= 0
|
||||
@body = StringIO.new(body)
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
@ -301,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
|
||||
|
||||
|
|
@ -338,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
|
||||
|
||||
|
|
@ -416,8 +415,7 @@ module Puma
|
|||
unless chunk
|
||||
@body.close
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
raise EOFError
|
||||
end
|
||||
|
||||
|
|
@ -426,8 +424,7 @@ module Puma
|
|||
if remain <= 0
|
||||
@body.rewind
|
||||
@buffer = nil
|
||||
@requests_served += 1
|
||||
@ready = true
|
||||
set_ready
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
@ -436,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
|
||||
|
|
|
|||
|
|
@ -295,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
|
||||
|
||||
|
|
@ -375,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
|
||||
|
|
@ -444,6 +461,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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'puma/detect'
|
||||
|
||||
if Puma.jruby?
|
||||
require 'puma/java_io_buffer'
|
||||
else
|
||||
require 'puma/puma_http11'
|
||||
end
|
||||
require 'puma/puma_http11'
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
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
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
begin
|
||||
require 'io/wait'
|
||||
rescue LoadError
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
module Puma
|
||||
|
|
@ -177,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
|
||||
|
|
@ -215,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,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:
|
||||
|
|
@ -23,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.
|
||||
#
|
||||
|
|
@ -38,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`.
|
||||
|
|
@ -94,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
|
||||
|
|
@ -110,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.
|
||||
|
|
@ -294,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,
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ require 'puma/util'
|
|||
|
||||
require 'puma/puma_http11'
|
||||
|
||||
unless Puma.const_defined? "IOBuffer"
|
||||
require 'puma/io_buffer'
|
||||
end
|
||||
|
||||
require 'socket'
|
||||
|
||||
module Puma
|
||||
|
|
@ -79,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
|
||||
|
||||
|
|
@ -102,7 +97,6 @@ module Puma
|
|||
|
||||
def inherit_binder(bind)
|
||||
@binder = bind
|
||||
@own_binder = false
|
||||
end
|
||||
|
||||
def tcp_mode!
|
||||
|
|
@ -270,10 +264,11 @@ module Puma
|
|||
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
||||
end
|
||||
|
||||
@notify.close
|
||||
|
||||
if @status != :restart and @own_binder
|
||||
@binder.close
|
||||
# Prevent can't modify frozen IOError (RuntimeError)
|
||||
begin
|
||||
@notify.close
|
||||
rescue IOError
|
||||
# no biggy
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -398,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
|
||||
|
|
@ -430,10 +428,6 @@ module Puma
|
|||
ensure
|
||||
@check.close
|
||||
@notify.close
|
||||
|
||||
if @status != :restart and @own_binder
|
||||
@binder.close
|
||||
end
|
||||
end
|
||||
|
||||
@events.fire :state, :done
|
||||
|
|
@ -942,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
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ module Puma
|
|||
end
|
||||
|
||||
def stop
|
||||
@server.stop false
|
||||
@server.stop(false) if @server
|
||||
end
|
||||
|
||||
def halt
|
||||
|
|
@ -36,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?
|
||||
|
|
|
|||
|
|
@ -194,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
|
||||
|
|
@ -203,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,11 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -71,7 +71,28 @@ if ENV['CI']
|
|||
Minitest::Retry.use!
|
||||
end
|
||||
|
||||
module SkipTestsBasedOnRubyEngine
|
||||
module TestSkips
|
||||
|
||||
@@next_port = 9000
|
||||
|
||||
# usage: skip NO_FORK_MSG unless HAS_FORK
|
||||
# windows >= 2.6 fork is not defined, < 2.6 fork raises NotImplementedError
|
||||
HAS_FORK = ::Process.respond_to? :fork
|
||||
NO_FORK_MSG = "Kernel.fork isn't available on the #{RUBY_PLATFORM} platform"
|
||||
|
||||
# socket is required by puma
|
||||
# usage: skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
UNIX_SKT_EXIST = Object.const_defined? :UNIXSocket
|
||||
UNIX_SKT_MSG = "UnixSockets aren't available on the #{RUBY_PLATFORM} platform"
|
||||
|
||||
# usage: skip_unless_signal_exist? :USR2
|
||||
def skip_unless_signal_exist?(sig, bt: caller)
|
||||
signal = sig.to_s
|
||||
unless Signal.list.key? signal
|
||||
skip "Signal #{signal} isn't available on the #{RUBY_PLATFORM} platform", bt
|
||||
end
|
||||
end
|
||||
|
||||
# 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
|
||||
|
|
@ -98,6 +119,10 @@ module SkipTestsBasedOnRubyEngine
|
|||
end
|
||||
skip skip_msg, bt if skip_msg
|
||||
end
|
||||
|
||||
def next_port(incr = 1)
|
||||
@@next_port += incr
|
||||
end
|
||||
end
|
||||
|
||||
Minitest::Test.include SkipTestsBasedOnRubyEngine
|
||||
Minitest::Test.include TestSkips
|
||||
|
|
|
|||
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"]]
|
||||
}
|
||||
1
test/rackup/hello-stuck-ci.ru
Normal file
1
test/rackup/hello-stuck-ci.ru
Normal file
|
|
@ -0,0 +1 @@
|
|||
run lambda { |env| sleep 10; [200, {"Content-Type" => "text/plain"}, ["Hello World"]] }
|
||||
|
|
@ -54,4 +54,43 @@ class TestBinder < Minitest::Test
|
|||
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
|
||||
|
|
|
|||
|
|
@ -40,8 +40,11 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_control_for_tcp
|
||||
url = "tcp://127.0.0.1:9877/"
|
||||
cli = Puma::CLI.new ["-b", "tcp://127.0.0.1:9876",
|
||||
tcp = next_port
|
||||
cntl = next_port
|
||||
url = "tcp://127.0.0.1:#{cntl}/"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "tcp://127.0.0.1:#{tcp}",
|
||||
"--control", url,
|
||||
"--control-token", "",
|
||||
"test/rackup/lobster.ru"], @events
|
||||
|
|
@ -53,18 +56,22 @@ class TestCLI < Minitest::Test
|
|||
|
||||
wait_booted
|
||||
|
||||
s = TCPSocket.new "127.0.0.1", 9877
|
||||
s = TCPSocket.new "127.0.0.1", cntl
|
||||
s << "GET /stats HTTP/1.0\r\n\r\n"
|
||||
body = s.read
|
||||
s.close
|
||||
|
||||
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
|
||||
|
||||
ensure
|
||||
cli.launcher.stop
|
||||
t.join
|
||||
end
|
||||
|
||||
def test_control_clustered
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
skip NO_FORK_MSG unless HAS_FORK
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
|
@ -102,7 +109,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_control
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
|
@ -126,7 +133,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_control_stop
|
||||
skip_on :jruby, :windows, suffix: " - Puma::Binder::UNIXServer is not defined"
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
url = "unix://#{@tmp_path}"
|
||||
|
||||
cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
|
||||
|
|
@ -148,21 +155,20 @@ class TestCLI < Minitest::Test
|
|||
t.join
|
||||
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}",
|
||||
"--control", url,
|
||||
def control_gc_stats(uri, cntl)
|
||||
cli = Puma::CLI.new ["-b", uri,
|
||||
"--control", cntl,
|
||||
"--control-token", "",
|
||||
"test/rackup/lobster.ru"], @events
|
||||
|
||||
t = Thread.new { cli.run }
|
||||
t.abort_on_exception = true
|
||||
t = Thread.new do
|
||||
Thread.current.abort_on_exception = true
|
||||
cli.run
|
||||
end
|
||||
|
||||
wait_booted
|
||||
|
||||
s = UNIXSocket.new @tmp_path
|
||||
s = yield
|
||||
s << "GET /gc-stats HTTP/1.0\r\n\r\n"
|
||||
body = s.read
|
||||
s.close
|
||||
|
|
@ -177,15 +183,16 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
gc_count_before = gc_stats["count"].to_i
|
||||
|
||||
s = UNIXSocket.new @tmp_path
|
||||
s = yield
|
||||
s << "GET /gc HTTP/1.0\r\n\r\n"
|
||||
body = s.read # Ignored
|
||||
s.close
|
||||
|
||||
s = UNIXSocket.new @tmp_path
|
||||
s = yield
|
||||
s << "GET /gc-stats HTTP/1.0\r\n\r\n"
|
||||
body = s.read
|
||||
s.close
|
||||
|
||||
lines = body.split("\r\n")
|
||||
json_line = lines.detect { |l| l[0] == "{" }
|
||||
pairs = json_line.scan(/\"[^\"]+\": [^,]+/)
|
||||
|
|
@ -197,15 +204,35 @@ class TestCLI < Minitest::Test
|
|||
gc_count_after = gc_stats["count"].to_i
|
||||
|
||||
# Hitting the /gc route should increment the count by 1
|
||||
assert_equal gc_count_before + 1, gc_count_after
|
||||
assert(gc_count_before < gc_count_after, "make sure a gc has happened")
|
||||
|
||||
cli.launcher.stop
|
||||
ensure
|
||||
cli.launcher.stop if cli
|
||||
t.join
|
||||
end
|
||||
|
||||
def test_control_gc_stats_tcp
|
||||
skip_on :jruby, suffix: " - Hitting /gc route does not increment count"
|
||||
uri = "tcp://127.0.0.1:#{next_port}/"
|
||||
cntl_port = next_port
|
||||
cntl = "tcp://127.0.0.1:#{cntl_port}/"
|
||||
|
||||
control_gc_stats(uri, cntl) { TCPSocket.new "127.0.0.1", cntl_port }
|
||||
end
|
||||
|
||||
def test_control_gc_stats_unix
|
||||
skip_on :jruby, suffix: " - Hitting /gc route does not increment count"
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
|
||||
uri = "unix://#{@tmp_path2}"
|
||||
cntl = "unix://#{@tmp_path}"
|
||||
|
||||
control_gc_stats(uri, cntl) { UNIXSocket.new @tmp_path }
|
||||
end
|
||||
|
||||
def test_tmp_control
|
||||
skip_on :jruby
|
||||
url = "tcp://127.0.0.1:8232"
|
||||
skip_on :jruby, suffix: " - Unknown issue"
|
||||
|
||||
cli = Puma::CLI.new ["--state", @tmp_path, "--control", "auto"]
|
||||
cli.launcher.write_state
|
||||
|
||||
|
|
@ -221,7 +248,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_state_file_callback_filtering
|
||||
skip_on :jruby, :windows, suffix: " - worker mode not supported"
|
||||
skip NO_FORK_MSG unless HAS_FORK
|
||||
cli = Puma::CLI.new [ "--config", "test/config/state_file_testing_config.rb",
|
||||
"--state", @tmp_path ]
|
||||
cli.launcher.write_state
|
||||
|
|
@ -233,7 +260,7 @@ class TestCLI < Minitest::Test
|
|||
end
|
||||
|
||||
def test_state
|
||||
url = "tcp://127.0.0.1:8232"
|
||||
url = "tcp://127.0.0.1:#{next_port}"
|
||||
cli = Puma::CLI.new ["--state", @tmp_path, "--control", url]
|
||||
cli.launcher.write_state
|
||||
|
||||
|
|
|
|||
|
|
@ -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&no_tlsv1=false"
|
||||
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,16 +2,17 @@ require_relative "helper"
|
|||
|
||||
require "puma/cli"
|
||||
require "puma/control_cli"
|
||||
require "open3"
|
||||
|
||||
# These don't run on travis because they're too fragile
|
||||
|
||||
class TestIntegration < Minitest::Test
|
||||
|
||||
def setup
|
||||
@state_path = "test/test_puma.state"
|
||||
@bind_path = "test/test_server.sock"
|
||||
@control_path = "test/test_control.sock"
|
||||
@token = "xxyyzz"
|
||||
@tcp_port = 9998
|
||||
|
||||
@server = nil
|
||||
|
||||
|
|
@ -41,6 +42,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def server(argv)
|
||||
@tcp_port = next_port
|
||||
base = "#{Gem.ruby} -Ilib bin/puma"
|
||||
base.prepend("bundle exec ") if defined?(Bundler)
|
||||
cmd = "#{base} -b tcp://127.0.0.1:#{@tcp_port} #{argv}"
|
||||
|
|
@ -52,6 +54,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def start_forked_server(argv)
|
||||
@tcp_port = next_port
|
||||
pid = fork do
|
||||
exec "#{Gem.ruby} -I lib/ bin/puma -b tcp://127.0.0.1:#{@tcp_port} #{argv}"
|
||||
end
|
||||
|
|
@ -67,7 +70,6 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def restart_server_and_listen(argv)
|
||||
skip_on :windows
|
||||
server(argv)
|
||||
s = connect
|
||||
initial_reply = read_body(s)
|
||||
|
|
@ -115,7 +117,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_stop_via_pumactl
|
||||
skip_on :jruby, :windows
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.quiet
|
||||
|
|
@ -148,7 +150,10 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_phased_restart_via_pumactl
|
||||
skip_on :jruby, :windows, :ci, suffix: " - UNIX sockets are not recommended"
|
||||
skip NO_FORK_MSG unless HAS_FORK
|
||||
|
||||
# hello-stuck-ci uses sleep 10, hello-stuck uses sleep 60
|
||||
rackup = "test/rackup/hello-stuck#{ ENV['CI'] ? '-ci' : '' }.ru"
|
||||
|
||||
conf = Puma::Configuration.new do |c|
|
||||
c.quiet
|
||||
|
|
@ -157,7 +162,7 @@ class TestIntegration < Minitest::Test
|
|||
c.activate_control_app "unix://#{@control_path}", :auth_token => @token
|
||||
c.workers 2
|
||||
c.worker_shutdown_timeout 1
|
||||
c.rackup "test/rackup/hello-stuck.ru"
|
||||
c.rackup rackup
|
||||
end
|
||||
|
||||
l = Puma::Launcher.new conf, :events => @events
|
||||
|
|
@ -195,11 +200,11 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_kill_unknown_via_pumactl
|
||||
skip_on :jruby, :windows
|
||||
skip_on :jruby
|
||||
|
||||
# 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
|
||||
io = IO.popen("ls")
|
||||
io = IO.popen(windows? ? "dir" : "ls")
|
||||
safe_pid = io.pid
|
||||
Process.wait safe_pid
|
||||
|
||||
|
|
@ -210,17 +215,19 @@ class TestIntegration < Minitest::Test
|
|||
ccli.run
|
||||
end
|
||||
sout.rewind
|
||||
assert_match(/No pid '\d+' found/, sout.readlines.join(""))
|
||||
# windows bad URI(is not URI?)
|
||||
assert_match(/No pid '\d+' found|bad URI\(is not URI\?\)/, sout.readlines.join(""))
|
||||
assert_equal(1, e.status)
|
||||
end
|
||||
|
||||
def test_restart_closes_keepalive_sockets
|
||||
skip_unless_signal_exist? :USR2
|
||||
_, new_reply = restart_server_and_listen("-q test/rackup/hello.ru")
|
||||
assert_equal "Hello World", new_reply
|
||||
end
|
||||
|
||||
def test_restart_closes_keepalive_sockets_workers
|
||||
skip_on :jruby
|
||||
skip NO_FORK_MSG unless HAS_FORK
|
||||
_, new_reply = restart_server_and_listen("-q -w 2 test/rackup/hello.ru")
|
||||
assert_equal "Hello World", new_reply
|
||||
end
|
||||
|
|
@ -230,6 +237,7 @@ class TestIntegration < Minitest::Test
|
|||
# jruby has a bug where setting `nil` into the ENV or `delete` do not change the
|
||||
# next workers ENV
|
||||
skip_on :jruby
|
||||
skip_unless_signal_exist? :USR2
|
||||
|
||||
initial_reply, new_reply = restart_server_and_listen("-q test/rackup/hello-env.ru")
|
||||
|
||||
|
|
@ -239,7 +247,7 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_term_signal_exit_code_in_single_mode
|
||||
skip_on :jruby, :windows
|
||||
skip NO_FORK_MSG unless HAS_FORK
|
||||
|
||||
pid = start_forked_server("test/rackup/hello.ru")
|
||||
_, status = stop_forked_server(pid)
|
||||
|
|
@ -248,11 +256,38 @@ class TestIntegration < Minitest::Test
|
|||
end
|
||||
|
||||
def test_term_signal_exit_code_in_clustered_mode
|
||||
skip_on :jruby, :windows
|
||||
skip NO_FORK_MSG unless HAS_FORK
|
||||
|
||||
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|
|
||||
|
|
@ -740,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,6 +1,8 @@
|
|||
require_relative "helper"
|
||||
require "puma/minissl"
|
||||
require "puma/puma_http11"
|
||||
# net/http (loaded in helper) does not necessarily load OpenSSL
|
||||
require "openssl" unless Object.const_defined? :OpenSSL
|
||||
|
||||
#———————————————————————————————————————————————————————————————————————————————
|
||||
# NOTE: ALL TESTS BYPASSED IF DISABLE_SSL IS TRUE
|
||||
|
|
@ -113,13 +115,15 @@ class TestPumaServerSSL < Minitest::Test
|
|||
|
||||
def test_ssl_v3_rejection
|
||||
@http.ssl_version= :SSLv3
|
||||
assert_raises(OpenSSL::SSL::SSLError) do
|
||||
# Ruby 2.4.5 on Travis raises ArgumentError
|
||||
assert_raises(OpenSSL::SSL::SSLError, ArgumentError) 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
|
||||
msg = /wrong version number|no protocols available|version too low|unknown SSL method/
|
||||
assert_match(msg, @events.error.message) if @events.error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -147,7 +151,7 @@ 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
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ class TestPumaUnixSocket < Minitest::Test
|
|||
Path = "test/puma.sock"
|
||||
|
||||
def setup
|
||||
# UNIX sockets are not recommended on JRuby or Windows
|
||||
skip_on :jruby, :windows, suffix: " - UNIX sockets are not recommended"
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
@server = Puma::Server.new App
|
||||
@server.add_unix_listener Path
|
||||
@server.run
|
||||
|
|
|
|||
|
|
@ -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..."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue