polyphony 0.96 → 0.98
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Rakefile +1 -1
- data/examples/io/https_server.rb +2 -1
- data/examples/io/https_server_sni.rb +31 -0
- data/examples/io/https_server_sni_2.rb +41 -0
- data/examples/io/irb.rb +1 -1
- data/examples/pipes/http_server.rb +4 -1
- data/ext/polyphony/backend_common.c +30 -0
- data/ext/polyphony/backend_common.h +1 -0
- data/ext/polyphony/backend_io_uring.c +125 -0
- data/ext/polyphony/backend_libev.c +135 -0
- data/ext/polyphony/io_extensions.c +2 -0
- data/ext/polyphony/polyphony.c +10 -0
- data/ext/polyphony/polyphony.h +2 -0
- data/lib/polyphony/adapters/irb.rb +2 -44
- data/lib/polyphony/extensions/io.rb +14 -0
- data/lib/polyphony/extensions/socket.rb +30 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +4 -0
- data/test/test_io.rb +8 -0
- data/test/test_socket.rb +70 -10
- data/test/test_thread.rb +2 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf93e3d5dfd0a5e3a4a70ee66d62caddc1ea22e63cf1ce44a212a75f18c2ac76
|
4
|
+
data.tar.gz: 121f84bb920ccc6822cb43b772a68dd92b2f2d81ebf9e1a510c081481dd1e3b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 286d3cb30ecd19b8f046fe43761f6d2f243f3e3a8134d01ebe14fdfdf11d6d879000f51ba66c705b0ad425e3c6a51dff02781b47e8cf75e52bc4f0439dfffc0e
|
7
|
+
data.tar.gz: 7caf9e3b7294ece38c964dc0366011b07d8977f95ec8c92a22763b9ecccc33080de40b5ab19bb66223eb4bb27be0847c92879a173dceee1b3fda9e1b03b0cdcd
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 0.98 2023-03-08
|
2
|
+
|
3
|
+
- Add basic support for UDP sockets
|
4
|
+
- Fix `IO#read` to return empty string when reading length zero
|
5
|
+
- Fix hang on `require 'polyphony'` in irb session
|
6
|
+
|
7
|
+
## 0.97 2023-02-28
|
8
|
+
|
9
|
+
- Fix working with IRB (#5)
|
10
|
+
|
1
11
|
## 0.96 2023-02-11
|
2
12
|
- Rework Redis client adapter
|
3
13
|
- Fix working with Pry (#95, thanks @floriandejonckheere)
|
data/Rakefile
CHANGED
data/examples/io/https_server.rb
CHANGED
@@ -18,7 +18,8 @@ puts 'Serving HTTPS on port 1234'
|
|
18
18
|
spin_loop(interval: 1) { STDOUT << '.' }
|
19
19
|
|
20
20
|
# server.accept_loop do |socket|
|
21
|
-
|
21
|
+
server.accept_loop do |socket|
|
22
|
+
# while (socket = (server.accept)
|
22
23
|
spin do
|
23
24
|
while (data = socket.gets("\n", 8192))
|
24
25
|
if data.chomp.empty?
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'localhost/authority'
|
6
|
+
|
7
|
+
authority = Localhost::Authority.fetch
|
8
|
+
server_ctx = authority.server_context
|
9
|
+
server_ctx.servername_cb = proc { |_socket, name| server_ctx }
|
10
|
+
opts = {
|
11
|
+
reuse_addr: true,
|
12
|
+
dont_linger: true,
|
13
|
+
secure_context: server_ctx
|
14
|
+
}
|
15
|
+
|
16
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
|
17
|
+
|
18
|
+
puts 'Serving HTTPS on port 1234'
|
19
|
+
|
20
|
+
# server.accept_loop do |socket|
|
21
|
+
server.accept_loop do |socket|
|
22
|
+
# while (socket = (server.accept)
|
23
|
+
spin do
|
24
|
+
while (data = socket.gets("\n", 8192))
|
25
|
+
if data.chomp.empty?
|
26
|
+
socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
|
27
|
+
break
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'localhost/authority'
|
6
|
+
|
7
|
+
authority = Localhost::Authority.fetch
|
8
|
+
server_ctx = authority.server_context
|
9
|
+
|
10
|
+
resolver = spin_loop do
|
11
|
+
name, client = receive
|
12
|
+
client << server_ctx
|
13
|
+
end
|
14
|
+
|
15
|
+
server_ctx.servername_cb = proc do |_socket, name|
|
16
|
+
resolver << [name, Fiber.current]
|
17
|
+
receive
|
18
|
+
end
|
19
|
+
|
20
|
+
opts = {
|
21
|
+
reuse_addr: true,
|
22
|
+
dont_linger: true,
|
23
|
+
secure_context: server_ctx
|
24
|
+
}
|
25
|
+
|
26
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234, opts)
|
27
|
+
|
28
|
+
puts 'Serving HTTPS on port 1234'
|
29
|
+
|
30
|
+
# server.accept_loop do |socket|
|
31
|
+
server.accept_loop do |socket|
|
32
|
+
# while (socket = (server.accept)
|
33
|
+
spin do
|
34
|
+
while (data = socket.gets("\n", 8192))
|
35
|
+
if data.chomp.empty?
|
36
|
+
socket << "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 4\n\nfoo\n"
|
37
|
+
break
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/examples/io/irb.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'bundler/inline'
|
4
4
|
|
5
5
|
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
6
7
|
gem 'h1p'
|
7
8
|
gem 'polyphony', path: '.'
|
8
9
|
end
|
@@ -22,10 +23,12 @@ def handle_client(conn)
|
|
22
23
|
|
23
24
|
conn << "HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\nHello, world!\n"
|
24
25
|
end
|
26
|
+
rescue Errno::ECONNRESET
|
27
|
+
# ignore
|
25
28
|
rescue H1P::Error
|
26
29
|
puts 'Got invalid request, closing connection...'
|
27
30
|
ensure
|
28
|
-
conn.close
|
31
|
+
conn.close rescue nil
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
@@ -501,6 +501,36 @@ int backend_getaddrinfo(VALUE host, VALUE port, struct sockaddr **ai_addr) {
|
|
501
501
|
return addrinfo_result->ai_addrlen;
|
502
502
|
}
|
503
503
|
|
504
|
+
inline VALUE name_to_addrinfo(void *name, socklen_t len) {
|
505
|
+
switch(((struct sockaddr *)name)->sa_family) {
|
506
|
+
case AF_INET:
|
507
|
+
{
|
508
|
+
struct sockaddr_in *info = name;
|
509
|
+
char buf[INET_ADDRSTRLEN];
|
510
|
+
|
511
|
+
VALUE port = INT2NUM(ntohs(info->sin_port));
|
512
|
+
if (!inet_ntop(AF_INET, &info->sin_addr, buf, sizeof(buf)))
|
513
|
+
rb_raise(rb_eRuntimeError, "Failed to get AF_INET addr");
|
514
|
+
VALUE addr = rb_str_new_cstr(buf);
|
515
|
+
return rb_ary_new_from_args(4, rb_str_new_literal("AF_INET"), port, addr, addr);
|
516
|
+
RB_GC_GUARD(addr);
|
517
|
+
}
|
518
|
+
case AF_INET6:
|
519
|
+
{
|
520
|
+
struct sockaddr_in6 *info = name;
|
521
|
+
char buf[INET6_ADDRSTRLEN];
|
522
|
+
|
523
|
+
VALUE port = INT2NUM(ntohs(info->sin6_port));
|
524
|
+
if (!inet_ntop(AF_INET6, &info->sin6_addr, buf, sizeof(buf)))
|
525
|
+
rb_raise(rb_eRuntimeError, "Failed to get AF_INET addr");
|
526
|
+
VALUE addr = rb_str_new_cstr(buf);
|
527
|
+
return rb_ary_new_from_args(4, rb_str_new_literal("AF_INET6"), port, addr, addr);
|
528
|
+
RB_GC_GUARD(addr);
|
529
|
+
}
|
530
|
+
}
|
531
|
+
return Qnil;
|
532
|
+
}
|
533
|
+
|
504
534
|
inline struct backend_buffer_spec backend_get_buffer_spec(VALUE in, int rw) {
|
505
535
|
if (FIXNUM_P(in)) {
|
506
536
|
struct buffer_spec *spec = FIX2PTR(in);
|
@@ -148,5 +148,6 @@ void set_fd_blocking_mode(int fd, int blocking);
|
|
148
148
|
void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
|
149
149
|
void backend_setup_stats_symbols();
|
150
150
|
int backend_getaddrinfo(VALUE host, VALUE port, struct sockaddr **ai_addr);
|
151
|
+
VALUE name_to_addrinfo(void *name, socklen_t len);
|
151
152
|
|
152
153
|
#endif /* BACKEND_COMMON_H */
|
@@ -673,6 +673,67 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE buffer, VALUE length, VALUE pos)
|
|
673
673
|
return buffer_spec.raw ? INT2FIX(total) : buffer;
|
674
674
|
}
|
675
675
|
|
676
|
+
VALUE Backend_recvmsg(VALUE self, VALUE io, VALUE buffer, VALUE maxlen, VALUE pos, VALUE flags, VALUE maxcontrollen, VALUE opts) {
|
677
|
+
Backend_t *backend;
|
678
|
+
int fd;
|
679
|
+
rb_io_t *fptr;
|
680
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 0);
|
681
|
+
long total = 0;
|
682
|
+
|
683
|
+
GetBackend(self, backend);
|
684
|
+
backend_prepare_read_buffer(buffer, maxlen, &buffer_spec, FIX2INT(pos));
|
685
|
+
fd = fd_from_io(io, &fptr, 0, 0);
|
686
|
+
|
687
|
+
char addr_buffer[64];
|
688
|
+
struct iovec iov;
|
689
|
+
struct msghdr msg;
|
690
|
+
|
691
|
+
iov.iov_base = StringValuePtr(buffer);
|
692
|
+
iov.iov_len = maxlen;
|
693
|
+
|
694
|
+
msg.msg_name = addr_buffer;
|
695
|
+
msg.msg_namelen = sizeof(addr_buffer);
|
696
|
+
msg.msg_iov = &iov;
|
697
|
+
msg.msg_iovlen = 1;
|
698
|
+
msg.msg_control = 0;
|
699
|
+
msg.msg_controllen = 0;
|
700
|
+
msg.msg_flags = 0;
|
701
|
+
|
702
|
+
while (1) {
|
703
|
+
VALUE resume_value = Qnil;
|
704
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
705
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
706
|
+
int result;
|
707
|
+
int completed;
|
708
|
+
|
709
|
+
io_uring_prep_recvmsg(sqe, fd, &msg, NUM2INT(flags));
|
710
|
+
|
711
|
+
result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
712
|
+
completed = context_store_release(&backend->store, ctx);
|
713
|
+
if (!completed) {
|
714
|
+
context_attach_buffers(ctx, 1, &buffer);
|
715
|
+
RAISE_IF_EXCEPTION(resume_value);
|
716
|
+
return resume_value;
|
717
|
+
}
|
718
|
+
RB_GC_GUARD(resume_value);
|
719
|
+
|
720
|
+
if (result < 0)
|
721
|
+
rb_syserr_fail(-result, strerror(-result));
|
722
|
+
else {
|
723
|
+
total += result;
|
724
|
+
break;
|
725
|
+
}
|
726
|
+
}
|
727
|
+
|
728
|
+
if (!total) return Qnil;
|
729
|
+
|
730
|
+
if (!buffer_spec.raw) backend_finalize_string_buffer(buffer, &buffer_spec, total, fptr);
|
731
|
+
VALUE addr = name_to_addrinfo(msg.msg_name, msg.msg_namelen);
|
732
|
+
VALUE rflags = INT2NUM(msg.msg_flags);
|
733
|
+
return rb_ary_new_from_args(3, buffer, addr, rflags);
|
734
|
+
RB_GC_GUARD(addr);
|
735
|
+
}
|
736
|
+
|
676
737
|
VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
|
677
738
|
Backend_t *backend;
|
678
739
|
int fd;
|
@@ -809,6 +870,68 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE buffer, VALUE flags) {
|
|
809
870
|
return INT2FIX(buffer_spec.len);
|
810
871
|
}
|
811
872
|
|
873
|
+
VALUE Backend_sendmsg(VALUE self, VALUE io, VALUE buffer, VALUE flags, VALUE dest_sockaddr, VALUE controls) {
|
874
|
+
Backend_t *backend;
|
875
|
+
int fd;
|
876
|
+
rb_io_t *fptr;
|
877
|
+
|
878
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 1);
|
879
|
+
long left = buffer_spec.len;
|
880
|
+
int flags_int = FIX2INT(flags);
|
881
|
+
|
882
|
+
GetBackend(self, backend);
|
883
|
+
fd = fd_from_io(io, &fptr, 1, 0);
|
884
|
+
|
885
|
+
struct iovec iov;
|
886
|
+
struct msghdr msg;
|
887
|
+
|
888
|
+
iov.iov_base = buffer_spec.ptr;
|
889
|
+
iov.iov_len = buffer_spec.len;
|
890
|
+
|
891
|
+
if (dest_sockaddr != Qnil) {
|
892
|
+
msg.msg_name = RSTRING_PTR(dest_sockaddr);
|
893
|
+
msg.msg_namelen = RSTRING_LEN(dest_sockaddr);
|
894
|
+
}
|
895
|
+
else {
|
896
|
+
msg.msg_name = 0;
|
897
|
+
msg.msg_namelen = 0;
|
898
|
+
}
|
899
|
+
msg.msg_iov = &iov;
|
900
|
+
msg.msg_iovlen = 1;
|
901
|
+
msg.msg_control = 0;
|
902
|
+
msg.msg_controllen = 0;
|
903
|
+
msg.msg_flags = 0;
|
904
|
+
|
905
|
+
while (left > 0) {
|
906
|
+
VALUE resume_value = Qnil;
|
907
|
+
op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
|
908
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
909
|
+
int result;
|
910
|
+
int completed;
|
911
|
+
|
912
|
+
io_uring_prep_sendmsg(sqe, fd, &msg, flags_int);
|
913
|
+
|
914
|
+
result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
915
|
+
completed = context_store_release(&backend->store, ctx);
|
916
|
+
if (!completed) {
|
917
|
+
context_attach_buffers(ctx, 1, &buffer);
|
918
|
+
RAISE_IF_EXCEPTION(resume_value);
|
919
|
+
return resume_value;
|
920
|
+
}
|
921
|
+
RB_GC_GUARD(resume_value);
|
922
|
+
|
923
|
+
if (result < 0)
|
924
|
+
rb_syserr_fail(-result, strerror(-result));
|
925
|
+
else {
|
926
|
+
iov.iov_base += result;
|
927
|
+
iov.iov_len -= result;
|
928
|
+
left -= result;
|
929
|
+
}
|
930
|
+
}
|
931
|
+
|
932
|
+
return INT2FIX(buffer_spec.len);
|
933
|
+
}
|
934
|
+
|
812
935
|
VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE socket_class, int loop) {
|
813
936
|
int server_fd;
|
814
937
|
rb_io_t *server_fptr;
|
@@ -1727,9 +1850,11 @@ void Init_Backend(void) {
|
|
1727
1850
|
rb_define_method(cBackend, "read", Backend_read, 5);
|
1728
1851
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 2);
|
1729
1852
|
rb_define_method(cBackend, "recv", Backend_recv, 4);
|
1853
|
+
rb_define_method(cBackend, "recvmsg", Backend_recvmsg, 7);
|
1730
1854
|
rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
|
1731
1855
|
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 2);
|
1732
1856
|
rb_define_method(cBackend, "send", Backend_send, 3);
|
1857
|
+
rb_define_method(cBackend, "sendmsg", Backend_sendmsg, 5);
|
1733
1858
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1734
1859
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1735
1860
|
|
@@ -350,6 +350,72 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE buffer, VALUE length, VALUE pos)
|
|
350
350
|
return Backend_read(self, io, buffer, length, Qnil, pos);
|
351
351
|
}
|
352
352
|
|
353
|
+
VALUE Backend_recvmsg(VALUE self, VALUE io, VALUE buffer, VALUE maxlen, VALUE pos, VALUE flags, VALUE maxcontrollen, VALUE opts) {
|
354
|
+
Backend_t *backend;
|
355
|
+
struct libev_io watcher;
|
356
|
+
int fd;
|
357
|
+
rb_io_t *fptr;
|
358
|
+
|
359
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 0);
|
360
|
+
long total = 0;
|
361
|
+
VALUE switchpoint_result = Qnil;
|
362
|
+
|
363
|
+
GetBackend(self, backend);
|
364
|
+
backend_prepare_read_buffer(buffer, maxlen, &buffer_spec, FIX2INT(pos));
|
365
|
+
fd = fd_from_io(io, &fptr, 0, 1);
|
366
|
+
watcher.fiber = Qnil;
|
367
|
+
|
368
|
+
char addr_buffer[64];
|
369
|
+
struct iovec iov;
|
370
|
+
struct msghdr msg;
|
371
|
+
|
372
|
+
iov.iov_base = StringValuePtr(buffer);
|
373
|
+
iov.iov_len = maxlen;
|
374
|
+
|
375
|
+
msg.msg_name = addr_buffer;
|
376
|
+
msg.msg_namelen = sizeof(addr_buffer);
|
377
|
+
msg.msg_iov = &iov;
|
378
|
+
msg.msg_iovlen = 1;
|
379
|
+
msg.msg_control = 0;
|
380
|
+
msg.msg_controllen = 0;
|
381
|
+
msg.msg_flags = 0;
|
382
|
+
|
383
|
+
while (1) {
|
384
|
+
backend->base.op_count++;
|
385
|
+
ssize_t result = recvmsg(fd, &msg, NUM2INT(flags));
|
386
|
+
if (result < 0) {
|
387
|
+
int e = errno;
|
388
|
+
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
389
|
+
|
390
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_READ);
|
391
|
+
|
392
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
393
|
+
}
|
394
|
+
else {
|
395
|
+
switchpoint_result = backend_snooze(&backend->base);
|
396
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
397
|
+
|
398
|
+
if (!result) break; // EOF
|
399
|
+
|
400
|
+
total += result;
|
401
|
+
break;
|
402
|
+
}
|
403
|
+
}
|
404
|
+
|
405
|
+
if (!total) return Qnil;
|
406
|
+
|
407
|
+
if (!buffer_spec.raw) backend_finalize_string_buffer(buffer, &buffer_spec, total, fptr);
|
408
|
+
VALUE addr = name_to_addrinfo(msg.msg_name, msg.msg_namelen);
|
409
|
+
VALUE rflags = INT2NUM(msg.msg_flags);
|
410
|
+
|
411
|
+
return rb_ary_new_from_args(3, buffer, addr, rflags);
|
412
|
+
RB_GC_GUARD(addr);
|
413
|
+
RB_GC_GUARD(watcher.fiber);
|
414
|
+
RB_GC_GUARD(switchpoint_result);
|
415
|
+
error:
|
416
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
417
|
+
}
|
418
|
+
|
353
419
|
VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
354
420
|
Backend_t *backend;
|
355
421
|
struct libev_io watcher;
|
@@ -768,6 +834,73 @@ error:
|
|
768
834
|
return RAISE_EXCEPTION(switchpoint_result);
|
769
835
|
}
|
770
836
|
|
837
|
+
VALUE Backend_sendmsg(VALUE self, VALUE io, VALUE buffer, VALUE flags, VALUE dest_sockaddr, VALUE controls) {
|
838
|
+
Backend_t *backend;
|
839
|
+
struct libev_io watcher;
|
840
|
+
int fd;
|
841
|
+
rb_io_t *fptr;
|
842
|
+
VALUE switchpoint_result = Qnil;
|
843
|
+
|
844
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 1);
|
845
|
+
long left = buffer_spec.len;
|
846
|
+
int flags_int = FIX2INT(flags);
|
847
|
+
|
848
|
+
GetBackend(self, backend);
|
849
|
+
fd = fd_from_io(io, &fptr, 1, 0);
|
850
|
+
watcher.fiber = Qnil;
|
851
|
+
|
852
|
+
struct iovec iov;
|
853
|
+
struct msghdr msg;
|
854
|
+
|
855
|
+
iov.iov_base = buffer_spec.ptr;
|
856
|
+
iov.iov_len = buffer_spec.len;
|
857
|
+
|
858
|
+
if (dest_sockaddr != Qnil) {
|
859
|
+
msg.msg_name = RSTRING_PTR(dest_sockaddr);
|
860
|
+
msg.msg_namelen = RSTRING_LEN(dest_sockaddr);
|
861
|
+
}
|
862
|
+
else {
|
863
|
+
msg.msg_name = 0;
|
864
|
+
msg.msg_namelen = 0;
|
865
|
+
}
|
866
|
+
msg.msg_iov = &iov;
|
867
|
+
msg.msg_iovlen = 1;
|
868
|
+
msg.msg_control = 0;
|
869
|
+
msg.msg_controllen = 0;
|
870
|
+
msg.msg_flags = 0;
|
871
|
+
|
872
|
+
while (left > 0) {
|
873
|
+
backend->base.op_count++;
|
874
|
+
ssize_t result = sendmsg(fd, &msg, flags_int);
|
875
|
+
if (result < 0) {
|
876
|
+
int e = errno;
|
877
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
878
|
+
|
879
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_WRITE);
|
880
|
+
|
881
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
882
|
+
}
|
883
|
+
else {
|
884
|
+
iov.iov_base = (buffer_spec.ptr += result);
|
885
|
+
iov.iov_len -= result;
|
886
|
+
left -= result;
|
887
|
+
}
|
888
|
+
}
|
889
|
+
|
890
|
+
if (watcher.fiber == Qnil) {
|
891
|
+
switchpoint_result = backend_snooze(&backend->base);
|
892
|
+
|
893
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
894
|
+
}
|
895
|
+
|
896
|
+
RB_GC_GUARD(watcher.fiber);
|
897
|
+
RB_GC_GUARD(switchpoint_result);
|
898
|
+
|
899
|
+
return INT2FIX(buffer_spec.len);
|
900
|
+
error:
|
901
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
902
|
+
}
|
903
|
+
|
771
904
|
struct libev_rw_ctx {
|
772
905
|
int ref_count;
|
773
906
|
VALUE fiber;
|
@@ -1502,9 +1635,11 @@ void Init_Backend(void) {
|
|
1502
1635
|
rb_define_method(cBackend, "read", Backend_read, 5);
|
1503
1636
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 2);
|
1504
1637
|
rb_define_method(cBackend, "recv", Backend_recv, 4);
|
1638
|
+
rb_define_method(cBackend, "recvmsg", Backend_recvmsg, 7);
|
1505
1639
|
rb_define_method(cBackend, "recv_loop", Backend_read_loop, 2);
|
1506
1640
|
rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
|
1507
1641
|
rb_define_method(cBackend, "send", Backend_send, 3);
|
1642
|
+
rb_define_method(cBackend, "sendmsg", Backend_sendmsg, 5);
|
1508
1643
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1509
1644
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1510
1645
|
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -66,6 +66,10 @@ VALUE Polyphony_backend_recv(VALUE self, VALUE io, VALUE buffer, VALUE length, V
|
|
66
66
|
return Backend_recv(BACKEND(), io, buffer, length, pos);
|
67
67
|
}
|
68
68
|
|
69
|
+
VALUE Polyphony_backend_recvmsg(VALUE self, VALUE io, VALUE buffer, VALUE maxlen, VALUE pos, VALUE flags, VALUE maxcontrollen, VALUE opts) {
|
70
|
+
return Backend_recvmsg(BACKEND(), io, buffer, maxlen, pos, flags, maxcontrollen, opts);
|
71
|
+
}
|
72
|
+
|
69
73
|
VALUE Polyphony_backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
|
70
74
|
return Backend_recv_loop(BACKEND(), io, maxlen);
|
71
75
|
}
|
@@ -78,6 +82,10 @@ VALUE Polyphony_backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags) {
|
|
78
82
|
return Backend_send(BACKEND(), io, msg, flags);
|
79
83
|
}
|
80
84
|
|
85
|
+
VALUE Polyphony_backend_sendmsg(VALUE self, VALUE io, VALUE msg, VALUE flags, VALUE dest_sockaddr, VALUE controls) {
|
86
|
+
return Backend_sendmsg(BACKEND(), io, msg, flags, dest_sockaddr, controls);
|
87
|
+
}
|
88
|
+
|
81
89
|
VALUE Polyphony_backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
82
90
|
return Backend_sendv(BACKEND(), io, ary, flags);
|
83
91
|
}
|
@@ -181,9 +189,11 @@ void Init_Polyphony(void) {
|
|
181
189
|
rb_define_singleton_method(mPolyphony, "backend_read", Polyphony_backend_read, 5);
|
182
190
|
rb_define_singleton_method(mPolyphony, "backend_read_loop", Polyphony_backend_read_loop, 2);
|
183
191
|
rb_define_singleton_method(mPolyphony, "backend_recv", Polyphony_backend_recv, 4);
|
192
|
+
rb_define_singleton_method(mPolyphony, "backend_recvmsg", Polyphony_backend_recvmsg, 7);
|
184
193
|
rb_define_singleton_method(mPolyphony, "backend_recv_loop", Polyphony_backend_recv_loop, 2);
|
185
194
|
rb_define_singleton_method(mPolyphony, "backend_recv_feed_loop", Polyphony_backend_recv_feed_loop, 3);
|
186
195
|
rb_define_singleton_method(mPolyphony, "backend_send", Polyphony_backend_send, 3);
|
196
|
+
rb_define_singleton_method(mPolyphony, "backend_sendmsg", Polyphony_backend_sendmsg, 5);
|
187
197
|
rb_define_singleton_method(mPolyphony, "backend_sendv", Polyphony_backend_sendv, 3);
|
188
198
|
rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
|
189
199
|
rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -100,9 +100,11 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
|
|
100
100
|
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos);
|
101
101
|
VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen);
|
102
102
|
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos);
|
103
|
+
VALUE Backend_recvmsg(VALUE self, VALUE io, VALUE buffer, VALUE maxlen, VALUE pos, VALUE flags, VALUE maxcontrollen, VALUE opts);
|
103
104
|
VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen);
|
104
105
|
VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
|
105
106
|
VALUE Backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags);
|
107
|
+
VALUE Backend_sendmsg(VALUE self, VALUE io, VALUE msg, VALUE flags, VALUE dest_sockaddr, VALUE controls);
|
106
108
|
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
107
109
|
VALUE Backend_sleep(VALUE self, VALUE duration);
|
108
110
|
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
|
@@ -1,47 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
if Object.constants.include?(:Reline)
|
6
|
-
puts "reline"
|
7
|
-
class Reline::ANSI
|
8
|
-
def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
|
9
|
-
# p [:select, read_ios, timeout]
|
10
|
-
# puts caller.join("\n")
|
11
|
-
raise if read_ios.size > 1
|
12
|
-
raise if write_ios.size > 0
|
13
|
-
raise if error_ios.size > 0
|
14
|
-
|
15
|
-
# p 1
|
16
|
-
fiber = Fiber.current
|
17
|
-
timer = spin do
|
18
|
-
sleep timeout
|
19
|
-
fiber.cancel
|
20
|
-
end
|
21
|
-
# p 2
|
22
|
-
read_ios.each do |io|
|
23
|
-
# p wait: io
|
24
|
-
Polyphony.backend_wait_io(io, false)
|
25
|
-
# p :done_wait
|
26
|
-
return [io]
|
27
|
-
end
|
28
|
-
# p 3
|
29
|
-
rescue Polyphony::Cancel
|
30
|
-
# p :cancel
|
31
|
-
return nil
|
32
|
-
ensure
|
33
|
-
# p :ensure
|
34
|
-
timer.stop
|
35
|
-
# p :ensure_done
|
36
|
-
end
|
37
|
-
end
|
38
|
-
else
|
39
|
-
require_relative './readline'
|
40
|
-
|
41
|
-
# RubyLex patches
|
42
|
-
class ::RubyLex
|
43
|
-
class TerminateLineInput2 < RuntimeError
|
44
|
-
end
|
45
|
-
const_set(:TerminateLineInput, TerminateLineInput2)
|
46
|
-
end
|
3
|
+
module Kernel
|
4
|
+
alias_method :gets, :orig_gets
|
47
5
|
end
|
@@ -131,8 +131,20 @@ class ::IO
|
|
131
131
|
nil
|
132
132
|
end
|
133
133
|
|
134
|
+
def ungetc(c)
|
135
|
+
c = c.chr if c.is_a?(Integer)
|
136
|
+
if @read_buffer
|
137
|
+
@read_buffer.prepend(c)
|
138
|
+
else
|
139
|
+
@read_buffer = +c
|
140
|
+
end
|
141
|
+
end
|
142
|
+
alias_method :ungetbyte, :ungetc
|
143
|
+
|
134
144
|
alias_method :orig_read, :read
|
135
145
|
def read(len = nil, buf = nil, buf_pos = 0)
|
146
|
+
return '' if len == 0
|
147
|
+
|
136
148
|
if buf
|
137
149
|
return Polyphony.backend_read(self, buf, len, true, buf_pos)
|
138
150
|
end
|
@@ -252,6 +264,8 @@ class ::IO
|
|
252
264
|
end
|
253
265
|
|
254
266
|
def wait_readable(timeout = nil)
|
267
|
+
return self if @read_buffer && @read_buffer.size > 0
|
268
|
+
|
255
269
|
if timeout
|
256
270
|
move_on_after(timeout) do
|
257
271
|
Polyphony.backend_wait_io(self, false)
|
@@ -742,4 +742,33 @@ class ::UNIXSocket
|
|
742
742
|
def write_nonblock(buf, exception: true)
|
743
743
|
@io.write_nonblock(buf, exception: exception)
|
744
744
|
end
|
745
|
-
end
|
745
|
+
end
|
746
|
+
|
747
|
+
class ::UDPSocket
|
748
|
+
def recvfrom(maxlen, flags = 0)
|
749
|
+
buf = +''
|
750
|
+
Polyphony.backend_recvmsg(self, buf, maxlen, 0, flags, 0, nil)
|
751
|
+
end
|
752
|
+
|
753
|
+
def recvmsg(maxlen = nil, flags = 0, maxcontrollen = nil, opts = {})
|
754
|
+
buf = +''
|
755
|
+
Polyphony.backend_recvmsg(self, buf, maxlen || 4096, 0, flags, maxcontrollen, opts)
|
756
|
+
end
|
757
|
+
|
758
|
+
def sendmsg(msg, flags = 0, dest_sockaddr = nil, *controls)
|
759
|
+
Polyphony.backend_sendmsg(self, msg, flags, dest_sockaddr, controls)
|
760
|
+
end
|
761
|
+
|
762
|
+
def send(msg, flags, *addr)
|
763
|
+
sockaddr = case addr.size
|
764
|
+
when 2
|
765
|
+
Socket.sockaddr_in(addr[1], addr[0])
|
766
|
+
when 1
|
767
|
+
addr[0]
|
768
|
+
else
|
769
|
+
nil
|
770
|
+
end
|
771
|
+
|
772
|
+
Polyphony.backend_sendmsg(self, msg, flags, sockaddr, nil)
|
773
|
+
end
|
774
|
+
end
|
data/lib/polyphony/version.rb
CHANGED
data/lib/polyphony.rb
CHANGED
data/test/test_io.rb
CHANGED
@@ -93,6 +93,13 @@ class IOTest < MiniTest::Test
|
|
93
93
|
assert_equal 'deffoobar', buf
|
94
94
|
end
|
95
95
|
|
96
|
+
def test_read_zero
|
97
|
+
i, o = IO.pipe
|
98
|
+
|
99
|
+
o << 'hi'
|
100
|
+
assert_equal '', i.read(0)
|
101
|
+
end
|
102
|
+
|
96
103
|
def test_readpartial
|
97
104
|
i, o = IO.pipe
|
98
105
|
|
@@ -658,6 +665,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
658
665
|
|
659
666
|
# writing always causes snoozing
|
660
667
|
o << 'foo'
|
668
|
+
3.times { snooze }
|
661
669
|
o << 'bar'
|
662
670
|
o.close
|
663
671
|
|
data/test/test_socket.rb
CHANGED
@@ -4,11 +4,7 @@ require_relative 'helper'
|
|
4
4
|
require 'fileutils'
|
5
5
|
require 'msgpack'
|
6
6
|
|
7
|
-
class
|
8
|
-
def setup
|
9
|
-
super
|
10
|
-
end
|
11
|
-
|
7
|
+
class TCPSocketTest < MiniTest::Test
|
12
8
|
def start_tcp_server_on_random_port(host = '127.0.0.1')
|
13
9
|
port = rand(1100..60000)
|
14
10
|
server = TCPServer.new(host, port)
|
@@ -41,13 +37,13 @@ class SocketTest < MiniTest::Test
|
|
41
37
|
end
|
42
38
|
|
43
39
|
def test_tcpsocket_open_with_hostname
|
44
|
-
client = TCPSocket.open('
|
45
|
-
client.write("GET / HTTP/1.0\r\nHost:
|
40
|
+
client = TCPSocket.open('ipinfo.io', 80)
|
41
|
+
client.write("GET / HTTP/1.0\r\nHost: ipinfo.io\r\n\r\n")
|
46
42
|
result = nil
|
47
43
|
move_on_after(3) {
|
48
44
|
result = client.read
|
49
45
|
}
|
50
|
-
assert result =~ /HTTP\/1.0
|
46
|
+
assert result =~ /HTTP\/1.0 200 OK/
|
51
47
|
end
|
52
48
|
|
53
49
|
def test_tcp_ipv6
|
@@ -166,7 +162,9 @@ class SocketTest < MiniTest::Test
|
|
166
162
|
server_fiber&.await
|
167
163
|
server&.close
|
168
164
|
end
|
165
|
+
end
|
169
166
|
|
167
|
+
class UNIXSocketTest < MiniTest::Test
|
170
168
|
def test_unix_socket
|
171
169
|
path = '/tmp/test_unix_socket'
|
172
170
|
FileUtils.rm(path) rescue nil
|
@@ -193,7 +191,7 @@ class SocketTest < MiniTest::Test
|
|
193
191
|
end
|
194
192
|
end
|
195
193
|
|
196
|
-
class
|
194
|
+
class TCPSocketWithRawBufferTest < MiniTest::Test
|
197
195
|
def start_tcp_server_on_random_port(host = '127.0.0.1')
|
198
196
|
port = rand(1100..60000)
|
199
197
|
server = TCPServer.new(host, port)
|
@@ -243,6 +241,68 @@ class SocketWithRawBufferTest < MiniTest::Test
|
|
243
241
|
end
|
244
242
|
end
|
245
243
|
|
244
|
+
class UDPSocketTest < MiniTest::Test
|
245
|
+
def test_udp_recvfrom
|
246
|
+
u1 = UDPSocket.new
|
247
|
+
u1.bind('127.0.0.1', 0)
|
248
|
+
|
249
|
+
server_fiber = spin do
|
250
|
+
msg, peer_addr = u1.recvfrom(256)
|
251
|
+
u1.send(msg, 0, peer_addr[3], peer_addr[1])
|
252
|
+
end
|
253
|
+
|
254
|
+
server_addr = u1.addr
|
255
|
+
u2 = UDPSocket.new
|
256
|
+
u2.bind('127.0.0.1', 0)
|
257
|
+
u2.send('foobar', 0, server_addr[3], server_addr[1])
|
258
|
+
|
259
|
+
msg, addr = u2.recvfrom(256)
|
260
|
+
|
261
|
+
assert_equal 'foobar', msg
|
262
|
+
assert_equal server_addr, addr
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_udp_recvmsg
|
266
|
+
u1 = UDPSocket.new
|
267
|
+
u1.bind('127.0.0.1', 0)
|
268
|
+
|
269
|
+
server_fiber = spin do
|
270
|
+
msg, peer_addr = u1.recvmsg(256)
|
271
|
+
u1.send(msg, 0, peer_addr[3], peer_addr[1])
|
272
|
+
end
|
273
|
+
|
274
|
+
server_addr = u1.addr
|
275
|
+
u2 = UDPSocket.new
|
276
|
+
u2.bind('127.0.0.1', 0)
|
277
|
+
u2.send('foobar', 0, server_addr[3], server_addr[1])
|
278
|
+
|
279
|
+
msg, addr = u2.recvmsg(256)
|
280
|
+
|
281
|
+
assert_equal 'foobar', msg
|
282
|
+
assert_equal server_addr, addr
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_udp_recvfrom_ipv6
|
286
|
+
u1 = UDPSocket.new :INET6
|
287
|
+
u1.bind('::1', 0)
|
288
|
+
|
289
|
+
server_fiber = spin do
|
290
|
+
msg, peer_addr = u1.recvfrom(256)
|
291
|
+
u1.send(msg, 0, peer_addr[3], peer_addr[1])
|
292
|
+
end
|
293
|
+
|
294
|
+
server_addr = u1.addr
|
295
|
+
u2 = UDPSocket.new :INET6
|
296
|
+
u2.bind('::1', 0)
|
297
|
+
u2.send('foobar', 0, server_addr[3], server_addr[1])
|
298
|
+
|
299
|
+
msg, addr = u2.recvfrom(256)
|
300
|
+
|
301
|
+
assert_equal 'foobar', msg
|
302
|
+
assert_equal server_addr, addr
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
246
306
|
if IS_LINUX
|
247
307
|
class HTTPClientTest < MiniTest::Test
|
248
308
|
|
@@ -261,4 +321,4 @@ if IS_LINUX
|
|
261
321
|
assert_equal 'https://ipinfo.io/missingauth', response['readme']
|
262
322
|
end
|
263
323
|
end
|
264
|
-
end
|
324
|
+
end
|
data/test/test_thread.rb
CHANGED
@@ -40,13 +40,14 @@ class ThreadTest < MiniTest::Test
|
|
40
40
|
|
41
41
|
def test_thread_join_with_timeout
|
42
42
|
buffer = []
|
43
|
-
spin { (1..3).each { |i| snooze; buffer << i } }
|
43
|
+
f = spin { (1..3).each { |i| snooze; buffer << i } }
|
44
44
|
t = Thread.new { sleep 1; buffer << 4 }
|
45
45
|
t0 = Time.now
|
46
46
|
r = t.join(0.01)
|
47
47
|
t = nil
|
48
48
|
|
49
49
|
assert Time.now - t0 < 0.2
|
50
|
+
f.join
|
50
51
|
assert_equal [1, 2, 3], buffer
|
51
52
|
assert_nil r
|
52
53
|
ensure
|
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.
|
4
|
+
version: '0.98'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -250,6 +250,8 @@ files:
|
|
250
250
|
- examples/io/http1_splice_chunked.rb
|
251
251
|
- examples/io/httparty.rb
|
252
252
|
- examples/io/https_server.rb
|
253
|
+
- examples/io/https_server_sni.rb
|
254
|
+
- examples/io/https_server_sni_2.rb
|
253
255
|
- examples/io/irb.rb
|
254
256
|
- examples/io/net-http.rb
|
255
257
|
- examples/io/open.rb
|