puma 3.12.6-java → 4.0.0-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b02ea90141e52839bd36eedaff4e1064824a6c147571bab7fca3578547127eb
4
- data.tar.gz: 1f5652c39e101a7c412f5b3f3a3829b925bda5302cb0e5f12a84ae44c748528d
3
+ metadata.gz: 4c39ea51db133a83024a8ff2356aa674b278b43899806403017ee6e579cc7dc3
4
+ data.tar.gz: 790871a22c62426570bd73582809a42652ee5360c31b184d2278635e1f33162a
5
5
  SHA512:
6
- metadata.gz: 76d82ddad2ca48e4c46992c665af0abcf1dcf734a4b370033e7928f21e71d0368074cd108c94e5c076e58cbf129ad8a72464537167ab0ae39e437f0ef5c6df50
7
- data.tar.gz: 3739c36e080c6fb7c471a43c8edb153887deb85d1c7943a88ca42f3c8ab00340207405eef5008a84e2e04dfb1fee1be694575801af2a1e7878868e1958182f3b
6
+ metadata.gz: 5a62e84fa053a6975d602b4b8dad9644efffeeec38dbfe1dc29383d79a5d14791babefcbf70555e5c3ccc1ad5308352813a2f8487091223f5ea5cc6fa793add2
7
+ data.tar.gz: 9df82d47ee069859147ccc986f79105d39e80fb8854e3d19eb5c92ad3825d58ee0dd0a180556a07bb5c49c93eb4b44f878663d7be4aaa22687c202c119674961
data/History.md CHANGED
@@ -1,25 +1,29 @@
1
1
  ## Master
2
2
 
3
- * x features
4
-
5
- * x bugfixes
6
-
7
-
8
- ## 4.3.3 and 3.12.4 / 2020-02-28
9
- * Bugfixes
10
- * Fix: Fixes a problem where we weren't splitting headers correctly on newlines (#2132)
11
- * Security
12
- * Fix: Prevent HTTP Response splitting via CR in early hints.
13
-
14
- ## 4.3.2 and 3.12.3 / 2020-02-27
15
-
16
- * Security
17
- * Fix: Prevent HTTP Response splitting via CR/LF in header values. CVE-2020-5247.
18
-
19
- ## 4.3.1 and 3.12.2 / 2019-12-05
20
-
21
- * Security
22
- * Fix: a poorly-behaved client could use keepalive requests to monopolize Puma's reactor and create a denial of service attack. CVE-2019-16770.
3
+ x features
4
+ x bugfixes
5
+
6
+ ## 4.0.0 / 2019-06-25
7
+
8
+ 9 features
9
+ * Add support for disabling TLSv1.0 (#1562)
10
+ * Request body read time metric (#1569)
11
+ * Add out_of_band hook (#1648)
12
+ * Re-implement (native) IOBuffer for JRuby (#1691)
13
+ * Min worker timeout (#1716)
14
+ * Add option to suppress SignalException on SIGTERM (#1690)
15
+ * Allow mutual TLS CA to be set using `ssl_bind` DSL (#1689)
16
+ * Reactor now uses nio4r instead of `select` (#1728)
17
+ 9 x bugfixes
18
+ * Do not accept new requests on shutdown (#1685, #1808)
19
+ * Fix 3 corner cases when request body is chunked (#1508)
20
+ * Change pid existence check's condition branches (#1650)
21
+ * Don't call .stop on a server that doesn't exist (#1655)
22
+ * Implemented NID_X9_62_prime256v1 (P-256) curve over P-521 (#1671)
23
+ * Fix @notify.close can't modify frozen IOError (RuntimeError) (#1583)
24
+ * Fix Java 8 support (#1773)
25
+ * Fix error `uninitialized constant Puma::Cluster` (#1731)
26
+ * Fix `not_token` being able to be set to true (#1803)
23
27
 
24
28
  ## 3.12.1 / 2019-03-19
25
29
 
data/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  <p align="center">
2
- <img src="http://puma.io/images/logos/puma-logo-large.png">
2
+ <img src="https://puma.io/images/logos/puma-logo-large.png">
3
3
  </p>
4
4
 
5
5
  # Puma: A Ruby Web Server Built For Concurrency
6
6
 
7
7
  [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/puma/puma?utm\_source=badge&utm\_medium=badge&utm\_campaign=pr-badge)
8
- [![Build Status](https://secure.travis-ci.org/puma/puma.svg)](http://travis-ci.org/puma/puma)
9
- [![AppVeyor](https://img.shields.io/appveyor/ci/nateberkopec/puma.svg)](https://ci.appveyor.com/project/nateberkopec/puma)
10
- [![Dependency Status](https://gemnasium.com/puma/puma.svg)](https://gemnasium.com/puma/puma)
8
+ [![Travis Build Status](https://secure.travis-ci.org/puma/puma.svg)](https://travis-ci.org/puma/puma)
9
+ [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/0xnxc7a26u9b2bub/branch/master?svg=true)](https://ci.appveyor.com/project/puma/puma/branch/master)
11
10
  [![Code Climate](https://codeclimate.com/github/puma/puma.svg)](https://codeclimate.com/github/puma/puma)
11
+ [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=puma&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=puma&package-manager=bundler&version-scheme=semver)
12
12
 
13
13
  Puma is a **simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ruby/Rack applications** in development and production.
14
14
 
@@ -16,7 +16,7 @@ Puma is a **simple, fast, threaded, and highly concurrent HTTP 1.1 server for Ru
16
16
 
17
17
  Under the hood, Puma processes requests using a C-optimized Ragel extension (inherited from Mongrel) that provides fast, accurate HTTP 1.1 protocol parsing in a portable way. Puma then serves the request in a thread from an internal thread pool. Since each request is served in a separate thread, truly concurrent Ruby implementations (JRuby, Rubinius) will use all available CPU cores.
18
18
 
19
- Puma was designed to be the go-to server for [Rubinius](http://rubini.us), but also works well with JRuby and MRI.
19
+ Puma was designed to be the go-to server for [Rubinius](https://rubini.us), but also works well with JRuby and MRI.
20
20
 
21
21
  On MRI, there is a Global VM Lock (GVL) that ensures only one thread can run Ruby code at a time. But if you're doing a lot of blocking IO (such as HTTP calls to external APIs like Twitter), Puma still improves MRI's throughput by allowing blocking IO to be run concurrently.
22
22
 
@@ -25,7 +25,7 @@ On MRI, there is a Global VM Lock (GVL) that ensures only one thread can run Rub
25
25
  ```
26
26
  $ gem install puma
27
27
  $ puma <any rackup (*.ru) file>
28
- ```
28
+ ```
29
29
 
30
30
  ## Frameworks
31
31
 
@@ -160,18 +160,31 @@ Need a bit of security? Use SSL sockets:
160
160
  ```
161
161
  $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'
162
162
  ```
163
+
163
164
  #### Controlling SSL Cipher Suites
164
- Need to use or avoid specific SSL cipher suites? Use ssl_cipher_filter or ssl_cipher_list options.
165
- #####Ruby:
165
+
166
+ Need to use or avoid specific SSL cipher suites? Use `ssl_cipher_filter` or `ssl_cipher_list` options.
167
+
168
+ ##### Ruby:
169
+
166
170
  ```
167
171
  $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_filter=!aNULL:AES+SHA'
168
172
  ```
169
- #####JRuby:
173
+
174
+ ##### JRuby:
175
+
170
176
  ```
171
177
  $ 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'
172
178
  ```
179
+
173
180
  See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites.
174
181
 
182
+ Don't want to use insecure TLSv1.0 ?
183
+
184
+ ```
185
+ $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&no_tlsv1=true'
186
+ ```
187
+
175
188
  ### Control/Status Server
176
189
 
177
190
  Puma has a built-in status/control app that can be used to query and control Puma itself.
@@ -249,6 +262,13 @@ reliability in production environments:
249
262
  * [tools/jungle](https://github.com/puma/puma/tree/master/tools/jungle) for sysvinit (init.d) and upstart
250
263
  * [docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md)
251
264
 
265
+ ## Community Plugins
266
+
267
+ * [puma-heroku](https://github.com/evanphx/puma-heroku) — default Puma configuration for running on Heroku
268
+ * [puma-metrics](https://github.com/harmjanblok/puma-metrics) — export Puma metrics to Prometheus
269
+ * [puma-plugin-statsd](https://github.com/yob/puma-plugin-statsd) — send Puma metrics to statsd
270
+ * [puma-plugin-systemd](https://github.com/sj26/puma-plugin-systemd) — deeper integration with systemd for notify, status and watchdog
271
+
252
272
  ## Contributing
253
273
 
254
274
  To run the test suite:
data/docs/architecture.md CHANGED
@@ -20,6 +20,7 @@ Clustered mode is shown/discussed here. Single mode is analogous to having a sin
20
20
  * By default, a single, separate thread is used to receive HTTP requests across the socket.
21
21
  * When at least one worker thread is available for work, a connection is accepted and placed in this request buffer
22
22
  * This thread waits for entire HTTP requests to be received over the connection
23
+ * 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)
23
24
  * Once received, the connection is pushed into the "todo" set
24
25
  * Worker threads pop work off the "todo" set for processing
25
26
  * The thread processes the request via the rack application (which generates the HTTP response)
data/docs/deployment.md CHANGED
@@ -38,22 +38,42 @@ Here are some rules of thumb:
38
38
  * As you grow more confident in the thread safety of your app, you can tune the
39
39
  workers down and the threads up.
40
40
 
41
+ #### Ubuntu / Systemd (Systemctl) Installation
42
+
43
+ See [systemd.md](systemd.md)
44
+
41
45
  #### Worker utilization
42
46
 
43
- **How do you know if you're got enough (or too many workers)?**
47
+ **How do you know if you've got enough (or too many workers)?**
44
48
 
45
49
  A good question. Due to MRI's GIL, only one thread can be executing Ruby code at a time.
46
50
  But since so many apps are waiting on IO from DBs, etc., they can utilize threads
47
51
  to make better use of the process.
48
52
 
49
53
  The rule of thumb is you never want processes that are pegged all the time. This
50
- means that there is more work to do that the process can get through. On the other
54
+ means that there is more work to do than the process can get through. On the other
51
55
  hand, if you have processes that sit around doing nothing, then they're just eating
52
56
  up resources.
53
57
 
54
- Watching your CPU utilization over time and aim for about 70% on average. This means
58
+ Watch your CPU utilization over time and aim for about 70% on average. This means
55
59
  you've got capacity still but aren't starving threads.
56
60
 
61
+ **Measuring utilization**
62
+
63
+ Using a timestamp header from an upstream proxy server (eg. nginx or haproxy), it's
64
+ possible to get an indication of how long requests have been waiting for a Puma
65
+ thread to become available.
66
+
67
+ * Have your upstream proxy set a header with the time it received the request:
68
+ * nginx: `proxy_set_header X-Request-Start "${msec}";`
69
+ * haproxy: `http-request set-header X-Request-Start "%t";`
70
+ * In your Rack middleware, determine the amount of time elapsed since `X-Request-Start`.
71
+ * To improve accuracy, you will want to subtract time spent waiting for slow clients:
72
+ * `env['puma.request_body_wait']` contains the number of milliseconds Puma spent
73
+ waiting for the client to send the request body.
74
+ * haproxy: `%Th` (TLS handshake time) and `%Ti` (idle time before request) can
75
+ can also be added as headers.
76
+
57
77
  ## Daemonizing
58
78
 
59
79
  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`,
62
82
  puma does not require daemonization to do zero-downtime restarts.
63
83
 
64
84
  I see people using daemonization because they start puma directly via capistrano
65
- task and thus want it to live on past the `cap deploy`. To this people I said:
85
+ task and thus want it to live on past the `cap deploy`. To these people I say:
66
86
  You need to be using a process monitor. Nothing is making sure puma stays up in
67
87
  this scenario! You're just waiting for something weird to happen, puma to die,
68
88
  and to get paged at 3am. Do yourself a favor, at least the process monitoring
Binary file
Binary file
data/docs/restart.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  To perform a restart, there are 3 builtin mechanisms:
4
4
 
5
- * Send the `puma` process the `SIGUSR2` signal
6
- * Send the `puma` process the `SIGUSR1` signal (rolling restart, cluster mode only)
5
+ * Send the `puma` process the `SIGUSR2` signal (normal restart)
6
+ * Send the `puma` process the `SIGUSR1` signal (restart in phases (a "rolling restart"), cluster mode only)
7
7
  * Use the status server and issue `/restart`
8
8
 
9
9
  No code is shared between the current and restarted process, so it should be safe to issue a restart any place where you would manually stop Puma and start it again.
@@ -22,6 +22,8 @@ But again beware, upgrading an application sometimes involves upgrading the data
22
22
 
23
23
  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)).
24
24
 
25
+ **Note**: Hot and phased restarts are only available on MRI, not on JRuby. They are also unavailable on Windows servers.
26
+
25
27
  ### Release Directory
26
28
 
27
29
  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.
data/docs/systemd.md CHANGED
@@ -32,21 +32,26 @@ Type=simple
32
32
  # Preferably configure a non-privileged user
33
33
  # User=
34
34
 
35
- # The path to the puma application root
36
- # Also replace the "<WD>" place holders below with this path.
37
- WorkingDirectory=
35
+ # The path to the your application code root directory.
36
+ # Also replace the "<YOUR_APP_PATH>" place holders below with this path.
37
+ # Example /home/username/myapp
38
+ WorkingDirectory=<YOUR_APP_PATH>
38
39
 
39
40
  # Helpful for debugging socket activation, etc.
40
41
  # Environment=PUMA_DEBUG=1
41
42
 
42
- # The command to start Puma. This variant uses a binstub generated via
43
- # `bundle binstubs puma --path ./sbin` in the WorkingDirectory
44
- # (replace "<WD>" below)
45
- ExecStart=<WD>/sbin/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
43
+ # SystemD will not run puma even if it is in your path. You must specify
44
+ # an absolute URL to puma. For example /usr/local/bin/puma
45
+ # Alternatively, create a binstub with `bundle binstubs puma --path ./sbin` in the WorkingDirectory
46
+ ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/puma.rb
47
+
48
+ # Variant: Rails start.
49
+ # ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/config/puma.rb ../config.ru
46
50
 
47
- # Variant: Use config file with `bind` directives instead:
48
- # ExecStart=<WD>/sbin/puma -C config.rb
49
51
  # Variant: Use `bundle exec --keep-file-descriptors puma` instead of binstub
52
+ # Variant: Specify directives inline.
53
+ # ExecStart=/<FULLPATH>/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
54
+
50
55
 
51
56
  Restart=always
52
57
 
@@ -66,6 +71,13 @@ listening sockets open across puma restarts and achieves graceful
66
71
  restarts, including when upgraded puma, and is compatible with both
67
72
  clustered mode and application preload.
68
73
 
74
+ **Note:** Any wrapper scripts which `exec`, or other indirections in
75
+ `ExecStart`, may result in activated socket file descriptors being closed
76
+ before they reach the puma master process. For example, if using `bundle exec`,
77
+ pass the `--keep-file-descriptors` flag. `bundle exec` can be avoided by using a
78
+ `puma` executable generated by `bundle binstubs puma`. This is tracked in
79
+ [#1499].
80
+
69
81
  **Note:** Socket activation doesn't currently work on jruby. This is
70
82
  tracked in [#1367].
71
83
 
@@ -247,6 +259,12 @@ PIDFile=<WD>/shared/tmp/pids/puma.pid
247
259
  # reconsider if you actually need the forking config.
248
260
  Restart=no
249
261
 
262
+ # `puma_ctl restart` wouldn't work without this. It's because `pumactl`
263
+ # changes PID on restart and systemd stops the service afterwards
264
+ # because of the PID change. This option prevents stopping after PID
265
+ # change.
266
+ RemainAfterExit=yes
267
+
250
268
  [Install]
251
269
  WantedBy=multi-user.target
252
270
  ~~~~
@@ -6,11 +6,13 @@ import org.jruby.Ruby;
6
6
  import org.jruby.runtime.load.BasicLibraryService;
7
7
 
8
8
  import org.jruby.puma.Http11;
9
+ import org.jruby.puma.IOBuffer;
9
10
  import org.jruby.puma.MiniSSL;
10
11
 
11
12
  public class PumaHttp11Service implements BasicLibraryService {
12
13
  public boolean basicLoad(final Ruby runtime) throws IOException {
13
14
  Http11.createHttp11(runtime);
15
+ IOBuffer.createIOBuffer(runtime);
14
16
  MiniSSL.createMiniSSL(runtime);
15
17
  return true;
16
18
  }
@@ -14,14 +14,12 @@
14
14
 
15
15
  /*
16
16
  * capitalizes all lower-case ASCII characters,
17
- * converts dashes to underscores, and underscores to commas.
17
+ * converts dashes to underscores.
18
18
  */
19
19
  static void snake_upcase_char(char *c)
20
20
  {
21
21
  if (*c >= 'a' && *c <= 'z')
22
22
  *c &= ~0x20;
23
- else if (*c == '_')
24
- *c = ',';
25
23
  else if (*c == '-')
26
24
  *c = '_';
27
25
  }
@@ -12,14 +12,12 @@
12
12
 
13
13
  /*
14
14
  * capitalizes all lower-case ASCII characters,
15
- * converts dashes to underscores, and underscores to commas.
15
+ * converts dashes to underscores.
16
16
  */
17
17
  static void snake_upcase_char(char *c)
18
18
  {
19
19
  if (*c >= 'a' && *c <= 'z')
20
20
  *c &= ~0x20;
21
- else if (*c == '_')
22
- *c = ',';
23
21
  else if (*c == '-')
24
22
  *c = '_';
25
23
  }
@@ -142,6 +142,7 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
142
142
  VALUE obj;
143
143
  SSL_CTX* ctx;
144
144
  SSL* ssl;
145
+ int ssl_options;
145
146
 
146
147
  ms_conn* conn = engine_alloc(self, &obj);
147
148
 
@@ -164,6 +165,10 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
164
165
  ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
165
166
  VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
166
167
 
168
+ ID sym_no_tlsv1 = rb_intern("no_tlsv1");
169
+ VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0);
170
+
171
+
167
172
  ctx = SSL_CTX_new(SSLv23_server_method());
168
173
  conn->ctx = ctx;
169
174
 
@@ -175,7 +180,12 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
175
180
  SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
176
181
  }
177
182
 
178
- 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);
183
+ 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;
184
+
185
+ if(RTEST(no_tlsv1)) {
186
+ ssl_options |= SSL_OP_NO_TLSv1;
187
+ }
188
+ SSL_CTX_set_options(ctx, ssl_options);
179
189
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
180
190
 
181
191
  if (!NIL_P(ssl_cipher_filter)) {
@@ -189,12 +199,18 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
189
199
  DH *dh = get_dh1024();
190
200
  SSL_CTX_set_tmp_dh(ctx, dh);
191
201
 
192
- #ifndef OPENSSL_NO_ECDH
193
- EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
202
+ #if OPENSSL_VERSION_NUMBER < 0x10002000L
203
+ // Remove this case if OpenSSL 1.0.1 (now EOL) support is no
204
+ // longer needed.
205
+ EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
194
206
  if (ecdh) {
195
207
  SSL_CTX_set_tmp_ecdh(ctx, ecdh);
196
208
  EC_KEY_free(ecdh);
197
209
  }
210
+ #elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
211
+ // Prior to OpenSSL 1.1.0, servers must manually enable server-side ECDH
212
+ // negotiation.
213
+ SSL_CTX_set_ecdh_auto(ctx, 1);
198
214
  #endif
199
215
 
200
216
  ssl = SSL_new(ctx);
@@ -217,7 +233,7 @@ VALUE engine_init_client(VALUE klass) {
217
233
  VALUE obj;
218
234
  ms_conn* conn = engine_alloc(klass, &obj);
219
235
 
220
- conn->ctx = SSL_CTX_new(DTLSv1_method());
236
+ conn->ctx = SSL_CTX_new(DTLS_method());
221
237
  conn->ssl = SSL_new(conn->ctx);
222
238
  SSL_set_app_data(conn->ssl, NULL);
223
239
  SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
@@ -0,0 +1,72 @@
1
+ package org.jruby.puma;
2
+
3
+ import org.jruby.*;
4
+ import org.jruby.anno.JRubyMethod;
5
+ import org.jruby.runtime.ObjectAllocator;
6
+ import org.jruby.runtime.ThreadContext;
7
+ import org.jruby.runtime.builtin.IRubyObject;
8
+ import org.jruby.util.ByteList;
9
+
10
+ /**
11
+ * @author kares
12
+ */
13
+ public class IOBuffer extends RubyObject {
14
+
15
+ private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
16
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
17
+ return new IOBuffer(runtime, klass);
18
+ }
19
+ };
20
+
21
+ public static void createIOBuffer(Ruby runtime) {
22
+ RubyModule mPuma = runtime.defineModule("Puma");
23
+ RubyClass cIOBuffer = mPuma.defineClassUnder("IOBuffer", runtime.getObject(), ALLOCATOR);
24
+ cIOBuffer.defineAnnotatedMethods(IOBuffer.class);
25
+ }
26
+
27
+ private static final int DEFAULT_SIZE = 4096;
28
+
29
+ final ByteList buffer = new ByteList(DEFAULT_SIZE);
30
+
31
+ IOBuffer(Ruby runtime, RubyClass klass) {
32
+ super(runtime, klass);
33
+ }
34
+
35
+ @JRubyMethod
36
+ public RubyInteger used(ThreadContext context) {
37
+ return context.runtime.newFixnum(buffer.getRealSize());
38
+ }
39
+
40
+ @JRubyMethod
41
+ public RubyInteger capacity(ThreadContext context) {
42
+ return context.runtime.newFixnum(buffer.unsafeBytes().length);
43
+ }
44
+
45
+ @JRubyMethod
46
+ public IRubyObject reset() {
47
+ buffer.setRealSize(0);
48
+ return this;
49
+ }
50
+
51
+ @JRubyMethod(name = { "to_s", "to_str" })
52
+ public RubyString to_s(ThreadContext context) {
53
+ return RubyString.newStringShared(context.runtime, buffer.unsafeBytes(), 0, buffer.getRealSize());
54
+ }
55
+
56
+ @JRubyMethod(name = "<<")
57
+ public IRubyObject add(IRubyObject str) {
58
+ addImpl(str.convertToString());
59
+ return this;
60
+ }
61
+
62
+ @JRubyMethod(rest = true)
63
+ public IRubyObject append(IRubyObject[] strs) {
64
+ for (IRubyObject str : strs) addImpl(str.convertToString());
65
+ return this;
66
+ }
67
+
68
+ private void addImpl(RubyString str) {
69
+ buffer.append(str.getByteList());
70
+ }
71
+
72
+ }