polyphony 0.86 → 0.90

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b87dae4e2c17055e8206c820a4281774cdf3127195173948c97df4eb09a2bd13
4
- data.tar.gz: 2e7bd958ec7435569423398069223eda41e9c2d89f580627bc22e9054276dd1c
3
+ metadata.gz: d9f125403a124d0374ebef23adfd4fe7f553767797003d63513ef780a248370b
4
+ data.tar.gz: 682134740af1614ac5e31d49c7d6a8ac8b881bbc26e5911e2f12a81f5ea52b27
5
5
  SHA512:
6
- metadata.gz: 48f6489939da965463735c83acb4372263eb44b48b5a16da1e704a15e3af971a5d43abd7da5e44f2455b40d880413c6fa8159794e663176c83a11eddebdebc7d
7
- data.tar.gz: 3ada5f9041041e91b6b2a7f31ec609352752317e87c51df090517c9e2b556d9ef69ef8842babd15f25b2257c4226431520ff15df505748cb38bb4a1d5a19063c
6
+ metadata.gz: d6292c9f2ddd6faf3d8df9565f8e6dce3be9ecd64fff95201bd2907cfacdb46eac5ef87a9281a3e88324471951493b6ebaa150dad40afb6a19958dd1e5992c0c
7
+ data.tar.gz: 10f8e607a0bd403664318e03abd4bbb4ab16704a69d1c0858ce5c9269c5c883e081dd95c51488dce7732ae471bf04cd139c0cbfb9623d6751a857551304e60f5
@@ -8,15 +8,19 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  os: [ubuntu-latest]
11
- ruby: [2.6, 2.7, 3.0]
11
+ ruby: ['2.7', '3.0', '3.1', 'head']
12
12
 
13
13
  name: >-
14
14
  ${{matrix.os}}, ${{matrix.ruby}}
15
15
 
16
16
  runs-on: ${{matrix.os}}
17
17
  steps:
18
- - uses: actions/checkout@v1
19
- - uses: ruby/setup-ruby@v1
18
+ - name: Checkout repository and submodules
19
+ uses: actions/checkout@v2
20
+ with:
21
+ submodules: recursive
22
+ - name: Setup Ruby
23
+ uses: ruby/setup-ruby@v1
20
24
  with:
21
25
  ruby-version: ${{matrix.ruby}}
22
26
  bundler-cache: true # 'bundle install' and cache
@@ -29,4 +33,4 @@ jobs:
29
33
  - name: Compile C-extension
30
34
  run: bundle exec rake compile
31
35
  - name: Run tests
32
- run: bundle exec rake test
36
+ run: bundle exec ruby test/run.rb --verbose --name test_sleep
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 0.90 2022-03-21
2
+
3
+ - Fix possible compilation error on Ruby 2.7.5 (#79)
4
+
5
+ ## 0.89 2022-03-21
6
+
7
+ - Implement compression/decompression to/from strings (#86)
8
+
9
+ ## 0.88 2022-03-18
10
+
11
+ - Improve IO stream compression utilities (release GVL, cleanup on exception)
12
+
13
+ ## 0.87 2022-03-17
14
+
15
+ - Fix compilation on non-Linux OSes
16
+
1
17
  ## 0.86 2022-03-14
2
18
 
3
19
  - Fix gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.86)
4
+ polyphony (0.90)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ require 'polyphony'
7
+
8
+ IO.http1_splice_chunked(STDIN, STDOUT, 16384)
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'polyphony'
6
+ require 'h1p'
7
+
8
+ server = Polyphony::Net.tcp_listen('localhost', 1234,
9
+ reuse_addr: true, reuse_port: true, dont_linger: true
10
+ )
11
+ puts 'Serving HTTP on port 1234'
12
+
13
+ def respond_default(conn)
14
+ conn << "HTTP/1.1 204\r\n\r\n"
15
+ end
16
+
17
+ def respond_splice(conn, path)
18
+ f = File.open(path, 'r') do |f|
19
+ conn << "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"
20
+ IO.http1_splice_chunked(f, conn, 16384)
21
+ end
22
+ rescue => e
23
+ p e
24
+ # conn << "HTTP/1.1 500\r\nContent-Length: 0\r\n\r\n"
25
+ end
26
+
27
+ def handle_client(conn)
28
+ parser = H1P::Parser.new(conn, :server)
29
+ while true
30
+ headers = parser.parse_headers
31
+ break unless headers
32
+
33
+ case headers[':path']
34
+ when /^\/splice\/(.+)$/
35
+ respond_splice(conn, $1)
36
+ else
37
+ respond_default(conn)
38
+ end
39
+ end
40
+ rescue Errno::ECONNRESET
41
+ # ignore
42
+ end
43
+
44
+ server.accept_loop do |conn|
45
+ handle_client(conn)
46
+ end
@@ -363,8 +363,6 @@ inline void set_fd_blocking_mode(int fd, int blocking) {
363
363
  }
364
364
 
365
365
  inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
366
- int flags;
367
- int is_nonblocking;
368
366
  VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
369
367
  if (blocking == blocking_mode) return;
370
368
 
@@ -481,7 +479,7 @@ struct io_buffer get_io_buffer(VALUE in) {
481
479
  struct raw_buffer *raw = FIX2PTR(in);
482
480
  return (struct io_buffer){ raw->ptr, raw->len, 1 };
483
481
  }
484
- return (struct io_buffer){ RSTRING_PTR(in), RSTRING_LEN(in), 0 };
482
+ return (struct io_buffer){ (unsigned char *)RSTRING_PTR(in), RSTRING_LEN(in), 0 };
485
483
  }
486
484
 
487
485
  VALUE coerce_io_string_or_buffer(VALUE buf) {
@@ -262,6 +262,21 @@ inline struct backend_stats backend_get_stats(VALUE self) {
262
262
  return backend_base_stats(&backend->base);
263
263
  }
264
264
 
265
+ static inline struct io_uring_sqe *io_uring_backend_get_sqe(Backend_t *backend) {
266
+ struct io_uring_sqe *sqe;
267
+ sqe = io_uring_get_sqe(&backend->ring);
268
+ if (sqe) goto done;
269
+
270
+ if (backend->pending_sqes)
271
+ io_uring_backend_immediate_submit(backend);
272
+ else {
273
+ VALUE resume_value = backend_snooze(&backend->base);
274
+ RAISE_IF_EXCEPTION(resume_value);
275
+ }
276
+ done:
277
+ return sqe;
278
+ }
279
+
265
280
  VALUE Backend_wakeup(VALUE self) {
266
281
  Backend_t *backend;
267
282
  GetBackend(self, backend);
@@ -269,7 +284,7 @@ VALUE Backend_wakeup(VALUE self) {
269
284
  if (backend->base.currently_polling) {
270
285
  // Since we're currently blocking while waiting for a completion, we add a
271
286
  // NOP which would cause the io_uring_enter syscall to return
272
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
287
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
273
288
  io_uring_prep_nop(sqe);
274
289
  io_uring_backend_immediate_submit(backend);
275
290
 
@@ -302,7 +317,7 @@ int io_uring_backend_defer_submit_and_await(
302
317
 
303
318
  // op was not completed (an exception was raised), so we need to cancel it
304
319
  ctx->result = -ECANCELED;
305
- sqe = io_uring_get_sqe(&backend->ring);
320
+ sqe = io_uring_backend_get_sqe(backend);
306
321
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
307
322
  io_uring_backend_immediate_submit(backend);
308
323
  }
@@ -317,7 +332,7 @@ VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
317
332
  op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
318
333
  VALUE resumed_value = Qnil;
319
334
 
320
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
335
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
321
336
  io_uring_prep_poll_add(sqe, fd, write ? POLLOUT : POLLIN);
322
337
 
323
338
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resumed_value);
@@ -368,7 +383,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
368
383
 
369
384
  if (string_cap < expected_read_length + buf_pos) {
370
385
  shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
371
- buffer.ptr = RSTRING_PTR(str) + buf_pos;
386
+ buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
372
387
  buffer.len = expected_read_length;
373
388
  }
374
389
  else {
@@ -385,7 +400,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
385
400
  while (1) {
386
401
  VALUE resume_value = Qnil;
387
402
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
388
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
403
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
389
404
  int result;
390
405
  int completed;
391
406
 
@@ -415,7 +430,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
415
430
  rb_str_resize(str, total + buf_pos);
416
431
  rb_str_modify_expand(str, rb_str_capacity(str));
417
432
  shrinkable_string = 0;
418
- buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
433
+ buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
419
434
  buffer.len = rb_str_capacity(str) - total - buf_pos;
420
435
  }
421
436
  else {
@@ -453,7 +468,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
453
468
  while (1) {
454
469
  VALUE resume_value = Qnil;
455
470
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
456
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
471
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
457
472
  ssize_t result;
458
473
  int completed;
459
474
 
@@ -502,7 +517,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
502
517
  while (1) {
503
518
  VALUE resume_value = Qnil;
504
519
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
505
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
520
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
506
521
  ssize_t result;
507
522
  int completed;
508
523
 
@@ -546,7 +561,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
546
561
  while (left > 0) {
547
562
  VALUE resume_value = Qnil;
548
563
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
549
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
564
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
550
565
  int result;
551
566
  int completed;
552
567
 
@@ -597,7 +612,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
597
612
  while (1) {
598
613
  VALUE resume_value = Qnil;
599
614
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
600
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
615
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
601
616
  int result;
602
617
  int completed;
603
618
 
@@ -672,7 +687,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
672
687
 
673
688
  if (string_cap < expected_read_length + buf_pos) {
674
689
  shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
675
- buffer.ptr = RSTRING_PTR(str) + buf_pos;
690
+ buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
676
691
  buffer.len = expected_read_length;
677
692
  }
678
693
  else {
@@ -689,7 +704,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
689
704
  while (1) {
690
705
  VALUE resume_value = Qnil;
691
706
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
692
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
707
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
693
708
  int result;
694
709
  int completed;
695
710
 
@@ -739,7 +754,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
739
754
  while (1) {
740
755
  VALUE resume_value = Qnil;
741
756
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
742
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
757
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
743
758
  int result;
744
759
  int completed;
745
760
 
@@ -787,7 +802,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
787
802
  while (1) {
788
803
  VALUE resume_value = Qnil;
789
804
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
790
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
805
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
791
806
  int result;
792
807
  int completed;
793
808
 
@@ -831,7 +846,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
831
846
  while (left > 0) {
832
847
  VALUE resume_value = Qnil;
833
848
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
834
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
849
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
835
850
  int result;
836
851
  int completed;
837
852
 
@@ -869,7 +884,7 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
869
884
  while (1) {
870
885
  VALUE resume_value = Qnil;
871
886
  op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
872
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
887
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
873
888
  int fd;
874
889
  int completed;
875
890
 
@@ -935,7 +950,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
935
950
 
936
951
  while (1) {
937
952
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
938
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
953
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
939
954
  int result;
940
955
  int completed;
941
956
 
@@ -984,7 +999,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
984
999
 
985
1000
  while (1) {
986
1001
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
987
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1002
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
988
1003
  int result;
989
1004
  int completed;
990
1005
 
@@ -1021,7 +1036,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
1021
1036
  GetBackend(self, backend);
1022
1037
  fd = fd_from_io(sock, &fptr, 1, 0);
1023
1038
  ctx = context_store_acquire(&backend->store, OP_CONNECT);
1024
- sqe = io_uring_get_sqe(&backend->ring);
1039
+ sqe = io_uring_backend_get_sqe(backend);
1025
1040
  io_uring_prep_connect(sqe, fd, ai_addr, ai_addrlen);
1026
1041
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1027
1042
  completed = context_store_release(&backend->store, ctx);
@@ -1063,7 +1078,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1063
1078
  // io_unset_nonblock(fptr, io);
1064
1079
 
1065
1080
  // ctx = context_store_acquire(&backend->store, OP_CLOSE);
1066
- // sqe = io_uring_get_sqe(&backend->ring);
1081
+ // sqe = io_uring_backend_get_sqe(backend);
1067
1082
  // io_uring_prep_close(sqe, fd);
1068
1083
  // result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1069
1084
  // completed = context_store_release(&backend->store, ctx);
@@ -1094,7 +1109,7 @@ inline struct __kernel_timespec duration_to_timespec(VALUE duration) {
1094
1109
  // returns true if completed, 0 otherwise
1095
1110
  int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
1096
1111
  struct __kernel_timespec ts = double_to_timespec(duration);
1097
- struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1112
+ struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
1098
1113
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
1099
1114
 
1100
1115
  io_uring_prep_timeout(sqe, &ts, 0, 0);
@@ -1183,7 +1198,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1183
1198
  GetBackend(self, backend);
1184
1199
  timeout = rb_funcall(cTimeoutException, ID_new, 0);
1185
1200
 
1186
- sqe = io_uring_get_sqe(&backend->ring);
1201
+ sqe = io_uring_backend_get_sqe(backend);
1187
1202
  ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
1188
1203
  ctx->resume_value = timeout;
1189
1204
  io_uring_prep_timeout(sqe, &ts, 0, 0);
@@ -1259,7 +1274,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1259
1274
  struct io_uring_sqe *sqe;
1260
1275
 
1261
1276
  backend->event_fd_ctx = context_store_acquire(&backend->store, OP_POLL);
1262
- sqe = io_uring_get_sqe(&backend->ring);
1277
+ sqe = io_uring_backend_get_sqe(backend);
1263
1278
  io_uring_prep_poll_add(sqe, backend->event_fd, POLLIN);
1264
1279
  backend->base.op_count++;
1265
1280
  io_uring_sqe_set_data(sqe, backend->event_fd_ctx);
@@ -1275,7 +1290,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1275
1290
 
1276
1291
  // last fiber to use the eventfd, so we cancel the ongoing poll
1277
1292
  struct io_uring_sqe *sqe;
1278
- sqe = io_uring_get_sqe(&backend->ring);
1293
+ sqe = io_uring_backend_get_sqe(backend);
1279
1294
  io_uring_prep_cancel(sqe, (__u64)backend->event_fd_ctx, 0);
1280
1295
  io_uring_backend_immediate_submit(backend);
1281
1296
  backend->event_fd_ctx = NULL;
@@ -1296,7 +1311,7 @@ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, V
1296
1311
  struct io_uring_sqe *sqe;
1297
1312
 
1298
1313
  fd = fd_from_io(io, &fptr, 1, 0);
1299
- sqe = io_uring_get_sqe(&backend->ring);
1314
+ sqe = io_uring_backend_get_sqe(backend);
1300
1315
  io_uring_prep_write(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), 0);
1301
1316
  return sqe;
1302
1317
  }
@@ -1308,7 +1323,7 @@ struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VA
1308
1323
 
1309
1324
  fd = fd_from_io(io, &fptr, 1, 0);
1310
1325
 
1311
- sqe = io_uring_get_sqe(&backend->ring);
1326
+ sqe = io_uring_backend_get_sqe(backend);
1312
1327
  io_uring_prep_send(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), NUM2INT(flags));
1313
1328
  return sqe;
1314
1329
  }
@@ -1322,7 +1337,7 @@ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src,
1322
1337
 
1323
1338
  src_fd = fd_from_io(src, &src_fptr, 0, 0);
1324
1339
  dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1325
- sqe = io_uring_get_sqe(&backend->ring);
1340
+ sqe = io_uring_backend_get_sqe(backend);
1326
1341
  io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
1327
1342
  return sqe;
1328
1343
  }
@@ -1381,7 +1396,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1381
1396
 
1382
1397
  ctx->ref_count = sqe_count;
1383
1398
  ctx->result = -ECANCELED;
1384
- sqe = io_uring_get_sqe(&backend->ring);
1399
+ sqe = io_uring_backend_get_sqe(backend);
1385
1400
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1386
1401
  io_uring_backend_immediate_submit(backend);
1387
1402
  }
@@ -1411,7 +1426,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1411
1426
 
1412
1427
  // op was not completed (an exception was raised), so we need to cancel it
1413
1428
  ctx->result = -ECANCELED;
1414
- sqe = io_uring_get_sqe(&backend->ring);
1429
+ sqe = io_uring_backend_get_sqe(backend);
1415
1430
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1416
1431
  io_uring_backend_immediate_submit(backend);
1417
1432
  RAISE_IF_EXCEPTION(resume_value);
@@ -1470,14 +1485,14 @@ static inline void splice_chunks_get_sqe(
1470
1485
  }
1471
1486
  else
1472
1487
  *ctx = context_store_acquire(&backend->store, type);
1473
- (*sqe) = io_uring_get_sqe(&backend->ring);
1488
+ (*sqe) = io_uring_backend_get_sqe(backend);
1474
1489
  }
1475
1490
 
1476
1491
  static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
1477
1492
  struct io_uring_sqe *sqe;
1478
1493
 
1479
1494
  ctx->result = -ECANCELED;
1480
- sqe = io_uring_get_sqe(&backend->ring);
1495
+ sqe = io_uring_backend_get_sqe(backend);
1481
1496
  io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1482
1497
  io_uring_backend_immediate_submit(backend);
1483
1498
  }
@@ -311,7 +311,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
311
311
 
312
312
  if (string_cap < expected_read_length + buf_pos) {
313
313
  shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
314
- buffer.ptr = RSTRING_PTR(str) + buf_pos;
314
+ buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
315
315
  buffer.len = expected_read_length;
316
316
  }
317
317
  else {
@@ -353,7 +353,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
353
353
  rb_str_resize(str, total + buf_pos);
354
354
  rb_str_modify_expand(str, rb_str_capacity(str));
355
355
  shrinkable_string = 0;
356
- buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
356
+ buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
357
357
  buffer.len = rb_str_capacity(str) - total - buf_pos;
358
358
  }
359
359
  else {
@@ -1645,6 +1645,9 @@ void Init_Backend() {
1645
1645
 
1646
1646
  rb_define_method(cBackend, "splice", Backend_splice, 3);
1647
1647
  rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1648
+ #ifdef POLYPHONY_LINUX
1649
+ rb_define_method(cBackend, "tee", Backend_tee, 3);
1650
+ #endif
1648
1651
 
1649
1652
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1650
1653
  rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
@@ -47,6 +47,7 @@ end
47
47
  $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
48
48
  if config[:io_uring]
49
49
  $defs << "-DPOLYPHONY_BACKEND_LIBURING"
50
+ $defs << "-DPOLYPHONY_LINUX"
50
51
  $defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
51
52
  $CFLAGS << " -Wno-pointer-arith"
52
53
  else
@@ -70,7 +71,8 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
70
71
 
71
72
  CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
72
73
 
73
- have_func('rb_fiber_transfer', 'ruby.h')
74
-
74
+ if RUBY_VERSION >= '3.1'
75
+ have_func('rb_fiber_transfer', 'ruby.h')
76
+ end
75
77
 
76
78
  create_makefile 'polyphony_ext'
@@ -10,6 +10,7 @@
10
10
  #include "polyphony.h"
11
11
  #include "zlib.h"
12
12
  #include "assert.h"
13
+ #include "ruby/thread.h"
13
14
 
14
15
  ID ID_at;
15
16
  ID ID_read_method;
@@ -29,6 +30,7 @@ VALUE SYM_orig_name;
29
30
  VALUE SYM_readpartial;
30
31
 
31
32
  enum read_method {
33
+ RM_STRING,
32
34
  RM_BACKEND_READ,
33
35
  RM_BACKEND_RECV,
34
36
  RM_READPARTIAL,
@@ -36,13 +38,15 @@ enum read_method {
36
38
  };
37
39
 
38
40
  enum write_method {
41
+ WM_STRING,
39
42
  WM_BACKEND_WRITE,
40
43
  WM_BACKEND_SEND,
41
44
  WM_WRITE,
42
45
  WM_CALL
43
46
  };
44
47
 
45
- static enum read_method detect_read_method(VALUE io) {
48
+ static inline enum read_method detect_read_method(VALUE io) {
49
+ if (TYPE(io) == T_STRING) return RM_STRING;
46
50
  if (rb_respond_to(io, ID_read_method)) {
47
51
  VALUE method = rb_funcall(io, ID_read_method, 0);
48
52
  if (method == SYM_readpartial) return RM_READPARTIAL;
@@ -58,7 +62,8 @@ static enum read_method detect_read_method(VALUE io) {
58
62
  rb_raise(rb_eRuntimeError, "Given io instance should be a callable or respond to #__read_method__");
59
63
  }
60
64
 
61
- static enum write_method detect_write_method(VALUE io) {
65
+ static inline enum write_method detect_write_method(VALUE io) {
66
+ if (TYPE(io) == T_STRING) return WM_STRING;
62
67
  if (rb_respond_to(io, ID_write_method)) {
63
68
  VALUE method = rb_funcall(io, ID_write_method, 0);
64
69
  if (method == SYM_readpartial) return WM_WRITE;
@@ -136,11 +141,18 @@ static inline int read_to_raw_buffer(VALUE backend, VALUE io, enum read_method m
136
141
  RB_GC_GUARD(str);
137
142
  return len;
138
143
  }
144
+ default: {
145
+ rb_raise(rb_eRuntimeError, "Invalid read method");
146
+ }
139
147
  }
140
148
  }
141
149
 
142
150
  static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_method method, struct raw_buffer *buffer) {
143
151
  switch (method) {
152
+ case WM_STRING: {
153
+ rb_str_buf_cat(io, (char *)buffer->ptr, buffer->len);
154
+ return buffer->len;
155
+ }
144
156
  case WM_BACKEND_WRITE: {
145
157
  VALUE len = Backend_write(backend, io, PTR2FIX(buffer));
146
158
  return FIX2INT(len);
@@ -165,6 +177,9 @@ static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_meth
165
177
  RB_GC_GUARD(str);
166
178
  return buffer->len;
167
179
  }
180
+ default: {
181
+ rb_raise(rb_eRuntimeError, "Invalid write method");
182
+ }
168
183
  }
169
184
  }
170
185
 
@@ -228,7 +243,7 @@ static inline time_t time_from_object(VALUE o) {
228
243
  return FIX2INT(rb_funcall(o, rb_intern("to_i"), 0));
229
244
  }
230
245
 
231
- int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
246
+ int gzip_prepare_header(struct gzip_header_ctx *ctx, unsigned char *buffer, int maxlen) {
232
247
  int len = 0;
233
248
  unsigned char flags = 0, extraflags = 0;
234
249
 
@@ -259,7 +274,7 @@ int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
259
274
  return len;
260
275
  }
261
276
 
262
- int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, char *buffer, int maxlen) {
277
+ static inline int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, unsigned char *buffer, int maxlen) {
263
278
  assert(maxlen >= GZIP_FOOTER_LEN);
264
279
 
265
280
  gzfile_set32(crc32, buffer);
@@ -287,8 +302,8 @@ struct z_stream_ctx {
287
302
 
288
303
  unsigned char in[CHUNK];
289
304
  unsigned char out[CHUNK];
290
- int in_pos;
291
- int out_pos;
305
+ unsigned int in_pos;
306
+ unsigned int out_pos;
292
307
  unsigned long in_total;
293
308
  unsigned long out_total;
294
309
 
@@ -297,8 +312,8 @@ struct z_stream_ctx {
297
312
 
298
313
  typedef int (*zlib_func)(z_streamp, int);
299
314
 
300
- void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, unsigned long *total_read) {
301
- int null_pos;
315
+ void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, unsigned int *in_pos, unsigned long *total_read) {
316
+ unsigned long null_pos;
302
317
  // find null terminator
303
318
  for (null_pos = *in_pos; null_pos < *total_read; null_pos++) {
304
319
  if (!buffer->ptr[null_pos]) break;
@@ -306,28 +321,37 @@ void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, un
306
321
  if (null_pos == *total_read)
307
322
  rb_raise(rb_eRuntimeError, "Invalid gzip header");
308
323
 
309
- *str = rb_str_new_cstr(buffer->ptr + *in_pos);
324
+ *str = rb_str_new_cstr((char *)buffer->ptr + *in_pos);
310
325
  *in_pos = null_pos + 1;
311
326
  }
312
327
 
313
328
  void gzip_read_header(struct z_stream_ctx *ctx, struct gzip_header_ctx *header_ctx) {
314
- struct raw_buffer in_buffer = { ctx->in, CHUNK };
329
+ struct raw_buffer in_buffer;
315
330
  int flags;
316
331
 
317
- while (ctx->in_total < 10) {
318
- int read = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
319
- if (read == 0) goto error;
320
- ctx->in_total += read;
332
+ if (ctx->src_read_method == RM_STRING) {
333
+ in_buffer.ptr = (unsigned char *)RSTRING_PTR(ctx->src);
334
+ in_buffer.len = RSTRING_LEN(ctx->src);
335
+ ctx->in_total = in_buffer.len;
321
336
  }
337
+ else {
338
+ in_buffer.ptr = ctx->in;
339
+ in_buffer.len = CHUNK;
340
+ while (ctx->in_total < 10) {
341
+ int read = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
342
+ if (read == 0) goto error;
343
+ ctx->in_total += read;
344
+ }
345
+ }
346
+
322
347
  // PRINT_BUFFER("read gzip header", ctx->in, ctx->in_total);
323
- if (ctx->in[0] != GZ_MAGIC1) goto error;
324
- if (ctx->in[1] != GZ_MAGIC2) goto error;
325
- if (ctx->in[2] != GZ_METHOD_DEFLATE) goto error;
326
- flags = ctx->in[3];
348
+ if (in_buffer.ptr[0] != GZ_MAGIC1) goto error;
349
+ if (in_buffer.ptr[1] != GZ_MAGIC2) goto error;
350
+ if (in_buffer.ptr[2] != GZ_METHOD_DEFLATE) goto error;
351
+ flags = in_buffer.ptr[3];
327
352
 
328
- unsigned long mtime = gzfile_get32(ctx->in + 4);
353
+ unsigned long mtime = gzfile_get32(in_buffer.ptr + 4);
329
354
  header_ctx->mtime = INT2FIX(mtime);
330
-
331
355
  ctx->in_pos = 10;
332
356
 
333
357
  if (flags & GZ_FLAG_ORIG_NAME)
@@ -344,6 +368,26 @@ error:
344
368
  rb_raise(rb_eRuntimeError, "Invalid gzip header");
345
369
  }
346
370
 
371
+ struct process_z_stream_ctx {
372
+ z_stream *strm;
373
+ int flags;
374
+ zlib_func fun;
375
+ int ret;
376
+ };
377
+
378
+ void *do_process_z_stream_without_gvl(void *ptr) {
379
+ struct process_z_stream_ctx *ctx = (struct process_z_stream_ctx *)ptr;
380
+
381
+ ctx->ret = (ctx->fun)(ctx->strm, ctx->flags);
382
+ return NULL;
383
+ }
384
+
385
+ static inline int process_without_gvl(zlib_func fun, z_stream *strm, int flags) {
386
+ struct process_z_stream_ctx ctx = { strm, flags, fun, 0 };
387
+ rb_thread_call_without_gvl2(do_process_z_stream_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
388
+ return ctx.ret;
389
+ }
390
+
347
391
  static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, int eof) {
348
392
  int ret;
349
393
  int written;
@@ -351,7 +395,7 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
351
395
 
352
396
  int avail_out_pre = ctx->strm.avail_out = CHUNK - ctx->out_pos;
353
397
  ctx->strm.next_out = ctx->out + ctx->out_pos;
354
- ret = fun(&ctx->strm, eof ? Z_FINISH : Z_NO_FLUSH);
398
+ ret = process_without_gvl(fun, &ctx->strm, eof ? Z_FINISH : Z_NO_FLUSH);
355
399
  assert(ret != Z_STREAM_ERROR);
356
400
  written = avail_out_pre - ctx->strm.avail_out;
357
401
  out_buffer.ptr = ctx->out;
@@ -372,12 +416,13 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
372
416
  return ctx->strm.avail_out;
373
417
  }
374
418
 
375
- void z_stream_io_loop(struct z_stream_ctx *ctx) {
419
+ VALUE z_stream_io_loop(struct z_stream_ctx *ctx) {
376
420
  zlib_func fun = (ctx->mode == SM_DEFLATE) ? deflate : inflate;
377
421
 
378
- if (ctx->in_total > ctx->in_pos) {
422
+ if ((ctx->src_read_method != RM_STRING) && (ctx->in_total > ctx->in_pos)) {
379
423
  // In bytes already read for parsing gzip header, so we need to process the
380
424
  // rest.
425
+
381
426
  ctx->strm.next_in = ctx->in + ctx->in_pos;
382
427
  ctx->strm.avail_in = ctx->in_total -= ctx->in_pos;
383
428
 
@@ -389,14 +434,27 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
389
434
  }
390
435
 
391
436
  while (1) {
392
- struct raw_buffer in_buffer = {ctx->in, CHUNK};
393
- ctx->strm.next_in = ctx->in;
394
- int read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
395
- if (!read_len) break;
396
- int eof = read_len < CHUNK;
397
-
398
- if (ctx->mode == SM_DEFLATE)
399
- ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
437
+ int eof;
438
+ int read_len;
439
+ if (ctx->src_read_method == RM_STRING) {
440
+ struct raw_buffer in_buffer = {
441
+ (unsigned char *)RSTRING_PTR(ctx->src) + ctx->in_pos,
442
+ RSTRING_LEN(ctx->src) - ctx->in_pos
443
+ };
444
+ ctx->strm.next_in = in_buffer.ptr;
445
+ read_len = ctx->strm.avail_in = in_buffer.len;
446
+ eof = 1;
447
+ if (ctx->mode == SM_DEFLATE) ctx->crc32 = crc32(ctx->crc32, in_buffer.ptr, read_len);
448
+ }
449
+ else {
450
+ struct raw_buffer in_buffer = {ctx->in, CHUNK};
451
+ ctx->strm.next_in = ctx->in;
452
+ read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
453
+ if (!read_len) break;
454
+ eof = read_len < CHUNK;
455
+ if (ctx->mode == SM_DEFLATE) ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
456
+ }
457
+
400
458
  ctx->in_total += read_len;
401
459
 
402
460
  // PRINT_BUFFER("read stream", ctx->in, read_len);
@@ -407,13 +465,15 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
407
465
  if (z_stream_write_out(ctx, fun, eof)) break;
408
466
  }
409
467
 
410
- if (eof) return;
468
+ if (eof) goto done;
411
469
  }
412
470
 
413
471
  //flush
414
472
  ctx->strm.avail_in = 0;
415
473
  ctx->strm.next_in = ctx->in;
416
474
  z_stream_write_out(ctx, fun, 1);
475
+ done:
476
+ return Qnil;
417
477
  }
418
478
 
419
479
  static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VALUE src, VALUE dest) {
@@ -434,6 +494,18 @@ static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VA
434
494
  ctx->crc32 = 0;
435
495
  }
436
496
 
497
+ static inline VALUE z_stream_cleanup(struct z_stream_ctx *ctx) {
498
+ if (ctx->mode == SM_DEFLATE)
499
+ deflateEnd(&ctx->strm);
500
+ else
501
+ inflateEnd(&ctx->strm);
502
+ return Qnil;
503
+ }
504
+
505
+ #define SAFE(f) (VALUE (*)(VALUE))(f)
506
+ #define Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx) \
507
+ rb_ensure(SAFE(z_stream_io_loop), (VALUE)&ctx, SAFE(z_stream_cleanup), (VALUE)&ctx)
508
+
437
509
  VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
438
510
  VALUE src;
439
511
  VALUE dest;
@@ -450,18 +522,16 @@ VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
450
522
  };
451
523
 
452
524
  struct z_stream_ctx ctx;
453
- int level = DEFAULT_LEVEL;
454
525
  int ret;
455
526
 
456
527
  setup_ctx(&ctx, SM_DEFLATE, src, dest);
457
528
  ctx.f_gzip_footer = 1; // write gzip footer
458
- ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
529
+ ctx.out_total = ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
459
530
 
460
- ret = deflateInit2(&ctx.strm, level, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
461
- if (ret != Z_OK) return INT2FIX(ret);
462
- z_stream_io_loop(&ctx);
463
- deflateEnd(&ctx.strm);
464
-
531
+ ret = deflateInit2(&ctx.strm, DEFAULT_LEVEL, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
532
+ if (ret != Z_OK)
533
+ rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
534
+ Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
465
535
  return INT2FIX(ctx.out_total);
466
536
  }
467
537
 
@@ -482,21 +552,25 @@ VALUE IO_gunzip(int argc, VALUE *argv, VALUE self) {
482
552
  setup_ctx(&ctx, SM_INFLATE, src, dest);
483
553
  gzip_read_header(&ctx, &header_ctx);
484
554
 
555
+ ret = inflateInit2(&ctx.strm, -MAX_WBITS);
556
+ if (ret != Z_OK)
557
+ rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
558
+
559
+ Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
560
+
561
+ // gzip_read_footer(&ctx, &footer_ctx);
562
+ // TODO: verify crc32
563
+ // TODO: verify total length
564
+
485
565
  if (info != Qnil) {
486
566
  rb_hash_aset(info, SYM_mtime, FIX2TIME(header_ctx.mtime));
487
567
  rb_hash_aset(info, SYM_orig_name, header_ctx.orig_name);
488
568
  rb_hash_aset(info, SYM_comment, header_ctx.comment);
489
569
  }
570
+ RB_GC_GUARD(header_ctx.orig_name);
571
+ RB_GC_GUARD(header_ctx.comment);
490
572
 
491
- ret = inflateInit2(&ctx.strm, -MAX_WBITS);
492
- if (ret != Z_OK) return INT2FIX(ret);
493
- z_stream_io_loop(&ctx);
494
- inflateEnd(&ctx.strm);
495
-
496
- // gzip_read_footer(&ctx, &footer_ctx);
497
- // TODO: verify crc32
498
- // TODO: verify total length
499
- return self;
573
+ return INT2FIX(ctx.out_total);
500
574
  }
501
575
 
502
576
  VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
@@ -506,9 +580,10 @@ VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
506
580
 
507
581
  setup_ctx(&ctx, SM_DEFLATE, src, dest);
508
582
  ret = deflateInit(&ctx.strm, level);
509
- if (ret != Z_OK) return INT2FIX(ret);
510
- z_stream_io_loop(&ctx);
511
- deflateEnd(&ctx.strm);
583
+ if (ret != Z_OK)
584
+ rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
585
+
586
+ Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
512
587
 
513
588
  return INT2FIX(ctx.out_total);
514
589
  }
@@ -519,19 +594,52 @@ VALUE IO_inflate(VALUE self, VALUE src, VALUE dest) {
519
594
 
520
595
  setup_ctx(&ctx, SM_INFLATE, src, dest);
521
596
  ret = inflateInit(&ctx.strm);
522
- if (ret != Z_OK) return INT2FIX(ret);
523
- z_stream_io_loop(&ctx);
524
- inflateEnd(&ctx.strm);
597
+ if (ret != Z_OK)
598
+ rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
599
+
600
+ Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
525
601
 
526
602
  return INT2FIX(ctx.out_total);
527
603
  }
528
604
 
605
+ VALUE IO_http1_splice_chunked(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
606
+ enum write_method method = detect_write_method(dest);
607
+ VALUE backend = BACKEND();
608
+ VALUE pipe = rb_funcall(cPipe, ID_new, 0);
609
+ unsigned char out[128];
610
+ struct raw_buffer buffer = { out, 0 };
611
+
612
+ while (1) {
613
+ int len = FIX2INT(Backend_splice(backend, src, pipe, maxlen));
614
+ if (!len) break;
615
+
616
+ // write chunk header
617
+ buffer.len += sprintf((char *)buffer.ptr + buffer.len, "%x\r\n", len);
618
+ write_from_raw_buffer(backend, dest, method, &buffer);
619
+ buffer.len = 0;
620
+ while (len) {
621
+ int spliced = FIX2INT(Backend_splice(backend, pipe, dest, INT2FIX(len)));
622
+ len -= spliced;
623
+ }
624
+ buffer.len += sprintf((char *)buffer.ptr + buffer.len, "\r\n");
625
+ }
626
+ buffer.len += sprintf((char *)buffer.ptr + buffer.len, "0\r\n\r\n");
627
+ write_from_raw_buffer(backend, dest, method, &buffer);
628
+
629
+ Pipe_close(pipe);
630
+ RB_GC_GUARD(pipe);
631
+
632
+ return self;
633
+ }
634
+
529
635
  void Init_IOExtensions() {
530
636
  rb_define_singleton_method(rb_cIO, "gzip", IO_gzip, -1);
531
637
  rb_define_singleton_method(rb_cIO, "gunzip", IO_gunzip, -1);
532
638
  rb_define_singleton_method(rb_cIO, "deflate", IO_deflate, 2);
533
639
  rb_define_singleton_method(rb_cIO, "inflate", IO_inflate, 2);
534
640
 
641
+ rb_define_singleton_method(rb_cIO, "http1_splice_chunked", IO_http1_splice_chunked, 3);
642
+
535
643
  ID_at = rb_intern("at");
536
644
  ID_read_method = rb_intern("__read_method__");
537
645
  ID_readpartial = rb_intern("readpartial");
@@ -94,9 +94,11 @@ VALUE Polyphony_backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE c
94
94
  return Backend_splice_to_eof(BACKEND(), src, dest, chunksize);
95
95
  }
96
96
 
97
+ #ifdef POLYPHONY_LINUX
97
98
  VALUE Polyphony_backend_tee(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
98
99
  return Backend_tee(BACKEND(), src, dest, chunksize);
99
100
  }
101
+ #endif
100
102
 
101
103
  VALUE Polyphony_backend_timeout(int argc,VALUE *argv, VALUE self) {
102
104
  return Backend_timeout(argc, argv, BACKEND());
@@ -143,7 +145,7 @@ VALUE Polyphony_raw_buffer_get(int argc, VALUE *argv, VALUE self) {
143
145
  int length = (len == Qnil) ? buffer->len : FIX2INT(len);
144
146
 
145
147
  if (length > buffer->len) length = buffer->len;
146
- return rb_utf8_str_new(buffer->ptr, length);
148
+ return rb_utf8_str_new((char *)buffer->ptr, length);
147
149
  }
148
150
 
149
151
  VALUE Polyphony_raw_buffer_set(VALUE self, VALUE buf, VALUE str) {
@@ -162,12 +164,6 @@ VALUE Polyphony_raw_buffer_size(VALUE self, VALUE buf) {
162
164
  return INT2FIX(buffer->len);
163
165
  }
164
166
 
165
- VALUE Polyphony_backend_test(VALUE self, VALUE io, VALUE str) {
166
- struct raw_buffer buffer = { RSTRING_PTR(str), RSTRING_LEN(str) };
167
- VALUE args[2] = { io, PTR2FIX(&buffer) };
168
- return Polyphony_backend_write(2, args, self);
169
- }
170
-
171
167
  // VALUE Polyphony_backend_close(VALUE self, VALUE io) {
172
168
  // return Backend_close(BACKEND(), io);
173
169
  // }
@@ -190,7 +186,11 @@ void Init_Polyphony() {
190
186
  rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
191
187
  rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
192
188
  rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
189
+
190
+ #ifdef POLYPHONY_LINUX
193
191
  rb_define_singleton_method(mPolyphony, "backend_tee", Polyphony_backend_tee, 3);
192
+ #endif
193
+
194
194
  rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
195
195
  rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
196
196
  rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
@@ -204,7 +204,6 @@ void Init_Polyphony() {
204
204
  rb_define_singleton_method(mPolyphony, "__raw_buffer_get__", Polyphony_raw_buffer_get, -1);
205
205
  rb_define_singleton_method(mPolyphony, "__raw_buffer_set__", Polyphony_raw_buffer_set, 2);
206
206
  rb_define_singleton_method(mPolyphony, "__raw_buffer_size__", Polyphony_raw_buffer_size, 1);
207
- rb_define_singleton_method(mPolyphony, "backend_test", Polyphony_backend_test, 2);
208
207
 
209
208
  rb_define_global_function("snooze", Polyphony_snooze, 0);
210
209
  rb_define_global_function("suspend", Polyphony_suspend, 0);
@@ -90,6 +90,7 @@ int Runqueue_should_poll_nonblocking(VALUE self);
90
90
 
91
91
  void Pipe_verify_blocking_mode(VALUE self, VALUE blocking);
92
92
  int Pipe_get_fd(VALUE self, int write_mode);
93
+ VALUE Pipe_close(VALUE self);
93
94
 
94
95
  #ifdef POLYPHONY_BACKEND_LIBEV
95
96
  #define Backend_recv_loop Backend_read_loop
@@ -112,7 +113,11 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
112
113
  VALUE Backend_sleep(VALUE self, VALUE duration);
113
114
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
114
115
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
116
+
117
+ #ifdef POLYPHONY_LINUX
115
118
  VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
119
+ #endif
120
+
116
121
  VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
117
122
  VALUE Backend_timer_loop(VALUE self, VALUE interval);
118
123
  VALUE Backend_wait_event(VALUE self, VALUE raise);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.86'
4
+ VERSION = '0.90'
5
5
  end
data/test/test_io.rb CHANGED
@@ -656,19 +656,75 @@ class IOExtensionsTest < MiniTest::Test
656
656
  i, o = IO.pipe
657
657
  r, w = IO.pipe
658
658
 
659
- spin {
660
- IO.deflate(i, w)
659
+ ret = nil
660
+ f = spin {
661
+ ret = IO.deflate(i, w)
661
662
  w.close
662
663
  }
663
664
 
664
665
  o << 'foobar' * 20
665
666
  o.close
666
667
 
668
+ f.await
669
+ assert_equal 17, ret
670
+
667
671
  data = r.read
668
672
  msg = Zlib::Inflate.inflate(data)
669
673
  assert_equal 'foobar' * 20, msg
670
674
  end
671
675
 
676
+ def test_deflate_to_string
677
+ i, o = IO.pipe
678
+ r, w = IO.pipe
679
+ str = +''
680
+
681
+ ret = nil
682
+ f = spin {
683
+ ret = IO.deflate(i, str)
684
+ w << str
685
+ w.close
686
+ }
687
+
688
+ o << 'foobar' * 20
689
+ o.close
690
+
691
+ f.await
692
+ assert_equal 17, ret
693
+
694
+ data = r.read
695
+ msg = Zlib::Inflate.inflate(data)
696
+ assert_equal 'foobar' * 20, msg
697
+ end
698
+
699
+ def test_deflate_to_frozen_string
700
+ i, o = IO.pipe
701
+ str = '' # frozen
702
+
703
+ f = spin {
704
+ o << 'foobar' * 20
705
+ o.close
706
+ }
707
+
708
+ assert_raises(FrozenError) { IO.deflate(i, str) }
709
+ end
710
+
711
+ def test_deflate_from_string
712
+ r, w = IO.pipe
713
+ str = 'foobar' * 10000
714
+ ret = nil
715
+
716
+ f = spin {
717
+ ret = IO.deflate(str, w)
718
+ w.close
719
+ }
720
+ f.await
721
+ assert_equal 118, ret
722
+
723
+ data = r.read
724
+ msg = Zlib::Inflate.inflate(data)
725
+ assert_equal str, msg
726
+ end
727
+
672
728
  def test_inflate
673
729
  i, o = IO.pipe
674
730
  r, w = IO.pipe
@@ -679,7 +735,35 @@ class IOExtensionsTest < MiniTest::Test
679
735
  o.close
680
736
  }
681
737
 
682
- IO.inflate(i, w)
738
+ ret = IO.inflate(i, w)
739
+ assert_equal 6, ret
740
+ w.close
741
+ msg = r.read
742
+ assert_equal 'foobar', msg
743
+ end
744
+
745
+ def test_inflate_to_string
746
+ i, o = IO.pipe
747
+ str = +''
748
+
749
+ spin {
750
+ data = Zlib::Deflate.deflate('foobar', 9)
751
+ o << data
752
+ o.close
753
+ }
754
+
755
+ ret = IO.inflate(i, str)
756
+ assert_equal 6, ret
757
+ assert_equal 6, str.bytesize
758
+ assert_equal 'foobar', str
759
+ end
760
+
761
+ def test_inflate_from_string
762
+ r, w = IO.pipe
763
+ str = Zlib::Deflate.deflate('foobar', 9)
764
+
765
+ ret = IO.inflate(str, w)
766
+ assert_equal 6, ret
683
767
  w.close
684
768
  msg = r.read
685
769
  assert_equal 'foobar', msg
@@ -690,7 +774,7 @@ class IOExtensionsTest < MiniTest::Test
690
774
  dest = Polyphony.pipe
691
775
  now = nil
692
776
 
693
- spin {
777
+ f = spin {
694
778
  now = Time.now
695
779
  IO.gzip(src, dest)
696
780
  dest.close
@@ -698,6 +782,32 @@ class IOExtensionsTest < MiniTest::Test
698
782
 
699
783
  src << IO.read(__FILE__)
700
784
  src.close
785
+ f.await
786
+
787
+ gz = Zlib::GzipReader.new(dest)
788
+ data = gz.read
789
+ assert_equal IO.read(__FILE__), data
790
+ assert_in_range (now-2)..(now+1), gz.mtime
791
+ assert_nil gz.orig_name
792
+ assert_nil gz.comment
793
+ end
794
+
795
+ def test_gzip_to_string
796
+ src = Polyphony.pipe
797
+ dest = Polyphony.pipe
798
+ str = +''
799
+ now = nil
800
+
801
+ f = spin {
802
+ now = Time.now
803
+ IO.gzip(src, str)
804
+ dest << str
805
+ dest.close
806
+ }
807
+
808
+ src << IO.read(__FILE__)
809
+ src.close
810
+ f.await
701
811
 
702
812
  gz = Zlib::GzipReader.new(dest)
703
813
  data = gz.read
@@ -707,6 +817,39 @@ class IOExtensionsTest < MiniTest::Test
707
817
  assert_nil gz.comment
708
818
  end
709
819
 
820
+ def test_gzip_from_string
821
+ str = IO.read(__FILE__)
822
+ dest = Polyphony.pipe
823
+ now = nil
824
+
825
+ IO.gzip(str, dest)
826
+ dest.close
827
+
828
+ gz = Zlib::GzipReader.new(dest)
829
+ data = gz.read
830
+ assert_equal IO.read(__FILE__), data
831
+ end
832
+
833
+ def test_gzip_return_value
834
+ src = Polyphony.pipe
835
+ dest = Polyphony.pipe
836
+ now = nil
837
+ ret = nil
838
+
839
+ f = spin {
840
+ now = Time.now
841
+ ret = IO.gzip(src, dest)
842
+ dest.close
843
+ }
844
+
845
+ src << IO.read(__FILE__)
846
+ src.close
847
+ f.await
848
+
849
+ gzipped = dest.read
850
+ assert_equal gzipped.bytesize, ret
851
+ end
852
+
710
853
  def test_gzip_with_mtime_int
711
854
  src = Polyphony.pipe
712
855
  dest = Polyphony.pipe
@@ -801,19 +944,51 @@ class IOExtensionsTest < MiniTest::Test
801
944
  def test_gunzip
802
945
  src = Polyphony.pipe
803
946
  dest = Polyphony.pipe
947
+ ret = nil
804
948
 
805
- spin {
806
- IO.gunzip(src, dest)
949
+ f = spin {
950
+ ret = IO.gunzip(src, dest)
807
951
  dest.close
808
952
  }
809
953
 
810
954
  gz = Zlib::GzipWriter.new(src, 9)
811
- gz << 'foobar'#IO.read(__FILE__)
955
+ gz << IO.read(__FILE__)
812
956
  gz.close
957
+ f.await
813
958
 
814
959
  data = dest.read
815
- # assert_equal IO.read(__FILE__), data
816
- assert_equal 'foobar', data
960
+ assert_equal IO.read(__FILE__).bytesize, ret
961
+ assert_equal IO.read(__FILE__), data
962
+ end
963
+
964
+ def test_gunzip_to_string
965
+ src = Polyphony.pipe
966
+ str = +''
967
+ ret = nil
968
+
969
+ f = spin {
970
+ ret = IO.gunzip(src, str)
971
+ }
972
+
973
+ gz = Zlib::GzipWriter.new(src, 9)
974
+ gz << IO.read(__FILE__)
975
+ gz.close
976
+ f.await
977
+
978
+ assert_equal IO.read(__FILE__).bytesize, ret
979
+ assert_equal IO.read(__FILE__), str
980
+ end
981
+
982
+ def test_gunzip_from_string
983
+ src_data = 'foobar' * 1000
984
+ str = Zlib.gzip(src_data, level: 9)
985
+ dest = Polyphony.pipe
986
+ ret = IO.gunzip(str, dest)
987
+ dest.close
988
+
989
+ dest_data = dest.read
990
+ assert_equal src_data.bytesize, ret
991
+ assert_equal src_data, dest_data
817
992
  end
818
993
 
819
994
  def test_gunzip_multi
@@ -893,4 +1068,24 @@ class IOExtensionsTest < MiniTest::Test
893
1068
  assert_equal 'foo.bar', dest_info[:orig_name]
894
1069
  assert_equal 'hello!', dest_info[:comment]
895
1070
  end
1071
+
1072
+ def test_deflate_inflate_strings
1073
+ src_data = IO.read(__FILE__)
1074
+ deflated = +''
1075
+ IO.deflate(src_data, deflated)
1076
+ inflated = +''
1077
+ IO.inflate(deflated, inflated)
1078
+
1079
+ assert_equal src_data, inflated
1080
+ end
1081
+
1082
+ def test_gzip_gunzip_strings
1083
+ src_data = IO.read(__FILE__)
1084
+ gzipped = +''
1085
+ IO.gzip(src_data, gzipped)
1086
+ gunzipped = +''
1087
+ IO.gunzip(gzipped, gunzipped)
1088
+
1089
+ assert_equal src_data, gunzipped
1090
+ end
896
1091
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.86'
4
+ version: '0.90'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-14 00:00:00.000000000 Z
11
+ date: 2022-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -262,6 +262,7 @@ files:
262
262
  - examples/io/echo_stdin.rb
263
263
  - examples/io/gzip.rb
264
264
  - examples/io/happy-eyeballs.rb
265
+ - examples/io/http1_splice_chunked.rb
265
266
  - examples/io/httparty.rb
266
267
  - examples/io/https_server.rb
267
268
  - examples/io/irb.rb
@@ -274,6 +275,7 @@ files:
274
275
  - examples/io/reline.rb
275
276
  - examples/io/splice_chunks.rb
276
277
  - examples/io/splice_echo_server.rb
278
+ - examples/io/static_web_server.rb
277
279
  - examples/io/stdio.rb
278
280
  - examples/io/system.rb
279
281
  - examples/io/tcp_proxy.rb