lightio 0.4.0.pre → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/Gemfile.lock +1 -1
- data/README.md +30 -4
- data/Rakefile +1 -0
- data/examples/http_requests.rb +21 -0
- data/lib/lightio/core/beam.rb +11 -2
- data/lib/lightio/library.rb +1 -0
- data/lib/lightio/library/base.rb +45 -0
- data/lib/lightio/library/io.rb +116 -92
- data/lib/lightio/library/kernel_ext.rb +62 -0
- data/lib/lightio/library/openssl.rb +26 -0
- data/lib/lightio/library/socket.rb +71 -4
- data/lib/lightio/module.rb +1 -0
- data/lib/lightio/module/base.rb +11 -5
- data/lib/lightio/module/openssl.rb +12 -0
- data/lib/lightio/module/socket.rb +2 -39
- data/lib/lightio/monkey.rb +39 -9
- data/lib/lightio/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bce34bd364d03347bca1a76cceede8fbad2838a
|
4
|
+
data.tar.gz: f99f66ed5d275bf5cd2e214faf131a5a391abb88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61e90a11df62adf44a9e28f380a4083671b086830474a2f142622d74e8d2c2925cdfdd0941487589cf8206f98b218521d1907acb0e6f730235574695e5fd52c1
|
7
|
+
data.tar.gz: 2d8d16a78c200c491bd1ab4eba245e10888354aab0a2bd04bd00c220a8b780b87e6bd70b870c92d5f31fac14bc3f6f9e889dc5a4cf3aca946ecb8a2c9dbf8ec3
|
data/.rspec
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -6,11 +6,37 @@
|
|
6
6
|
[](https://coveralls.io/github/socketry/lightio?branch=master)
|
7
7
|
[](https://github.com/jjyr/lightio/blob/master/LICENSE.txt)
|
8
8
|
|
9
|
-
LightIO
|
9
|
+
LightIO provide green thread to ruby. Like Golang's goroutine, or Crystal's fiber. In LightIO it called beam.
|
10
|
+
|
11
|
+
LightIO ship ruby stdlib compatible library under `LightIO` or `LightIO::Library` namespace,
|
12
|
+
these libraries provide ability to schedule LightIO beams when IO operations occur.
|
13
|
+
|
14
|
+
See [Examples](/examples) for detail.
|
15
|
+
|
16
|
+
LightIO also provide a monkey patch, it replace ruby `Thread` with `LightIO::Thread`, and also replace `IO` related classes.
|
17
|
+
|
18
|
+
Example:
|
19
|
+
|
20
|
+
``` ruby
|
21
|
+
LightIO::Monkey.patch_all!
|
22
|
+
# now you can just write normal ruby code
|
23
|
+
start = Time.now
|
24
|
+
10.times.map do
|
25
|
+
Thread.new do
|
26
|
+
`sleep 1`
|
27
|
+
end
|
28
|
+
end.each(&:join)
|
29
|
+
puts Time.now - start
|
30
|
+
|
31
|
+
```
|
32
|
+
|
33
|
+
### You Should Know
|
34
|
+
|
35
|
+
In fact ruby core team already plan to implement `Thread::Green` in core language, see https://bugs.ruby-lang.org/issues/13618
|
36
|
+
|
37
|
+
It mean if ruby implemented `Thread::Green`, this library is no meaning to exists.
|
38
|
+
But as a crazy userland implemented green thread library, it bring lot's of fun to me, so I will continue to maintain it, and welcome to use
|
10
39
|
|
11
|
-
* **Simple**, LightIO provide same API with ruby stdlib, from low level to high level, use `LightIO::Thread` to start green threads, use `LightIO::Socket` `LightIO::TCPServer` for IO operations.
|
12
|
-
* **Transparent**, LightIO encourage user to write multi threads and blocking-IO style code, LightIO will transfer actual IO operations to inner IO loop.
|
13
|
-
* **Monkey patch**(experiment), LightIO provide reversible monkey patch, call `LightIO::Monkey.patch_all!` to apply, it will let LightIO inner loop to take over all IO operations, and patch threads to light weight fibers.
|
14
40
|
|
15
41
|
See [Wiki](https://github.com/jjyr/lightio/wiki) and [Roadmap](https://github.com/jjyr/lightio/wiki/Current-status-and-roadmap) to get more information.
|
16
42
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'lightio'
|
2
|
+
# apply monkey patch at beginning
|
3
|
+
LightIO::Monkey.patch_all!
|
4
|
+
|
5
|
+
require 'net/http'
|
6
|
+
|
7
|
+
host = 'github.com'
|
8
|
+
port = 80
|
9
|
+
|
10
|
+
start = Time.now
|
11
|
+
|
12
|
+
10.times.map do
|
13
|
+
Thread.new do
|
14
|
+
Net::HTTP.start(host, port, use_ssl: false) do |http|
|
15
|
+
res = http.request_get('/ping')
|
16
|
+
p res.code
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end.each(&:join)
|
20
|
+
|
21
|
+
p Time.now - start
|
data/lib/lightio/core/beam.rb
CHANGED
@@ -88,10 +88,19 @@ module LightIO::Core
|
|
88
88
|
return self
|
89
89
|
end
|
90
90
|
|
91
|
+
# return to current beam if beam done within time limit
|
92
|
+
origin_parent = parent
|
93
|
+
self.parent = Beam.current
|
91
94
|
# set a transfer back timer
|
92
|
-
parent = Beam.current
|
93
95
|
timer = LightIO::Watchers::Timer.new(limit)
|
94
|
-
timer.set_callback
|
96
|
+
timer.set_callback do
|
97
|
+
if alive?
|
98
|
+
caller_beam = parent
|
99
|
+
# resume to origin parent
|
100
|
+
self.parent = origin_parent
|
101
|
+
caller_beam.transfer
|
102
|
+
end
|
103
|
+
end
|
95
104
|
ioloop.add_timer(timer)
|
96
105
|
ioloop.transfer
|
97
106
|
|
data/lib/lightio/library.rb
CHANGED
data/lib/lightio/library/base.rb
CHANGED
@@ -8,6 +8,7 @@ module LightIO::Library
|
|
8
8
|
define_method_missing(singleton_class, @mock_klass)
|
9
9
|
define_instance_method_missing(self, :@obj)
|
10
10
|
define_mock_methods
|
11
|
+
define_inherited
|
11
12
|
extend_class_methods
|
12
13
|
end
|
13
14
|
|
@@ -61,6 +62,27 @@ module LightIO::Library
|
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
65
|
+
def define_inherited
|
66
|
+
mock_klass.define_singleton_method(:inherited) do |klass|
|
67
|
+
super(klass)
|
68
|
+
library_super_class = LightIO::Module::Base.find_library_class(self)
|
69
|
+
library_klass = Class.new(library_super_class) do
|
70
|
+
include LightIO::Library::Base
|
71
|
+
mock klass
|
72
|
+
end
|
73
|
+
if klass.name
|
74
|
+
LightIO::Library::Base.send(:full_const_set, LightIO::Library, klass.name, library_klass)
|
75
|
+
else
|
76
|
+
LightIO::Library::Base.send(:nameless_classes)[klass] = library_klass
|
77
|
+
end
|
78
|
+
klass.define_singleton_method :new do |*args, &blk|
|
79
|
+
obj = library_klass.__send__ :allocate
|
80
|
+
obj.__send__ :initialize, *args, &blk
|
81
|
+
obj
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
64
86
|
def extend_class_methods
|
65
87
|
class_methods_module = LightIO::Module.const_get("#{mock_klass}::ClassMethods")
|
66
88
|
self.__send__ :extend, class_methods_module
|
@@ -86,11 +108,34 @@ module LightIO::Library
|
|
86
108
|
@obj = obj
|
87
109
|
end
|
88
110
|
|
111
|
+
private
|
112
|
+
def light_io_raw_obj
|
113
|
+
@obj
|
114
|
+
end
|
115
|
+
|
89
116
|
class << self
|
90
117
|
def included(base)
|
91
118
|
base.send :extend, MockMethods
|
92
119
|
base.send :extend, ClassMethods
|
93
120
|
end
|
121
|
+
|
122
|
+
private
|
123
|
+
def nameless_classes
|
124
|
+
@nick_classes ||= {}
|
125
|
+
end
|
126
|
+
|
127
|
+
def full_const_set(base, mod_name, const)
|
128
|
+
mods = mod_name.split("::")
|
129
|
+
mod_name = mods.pop
|
130
|
+
full_mod_name = base.to_s
|
131
|
+
mods.each do |mod|
|
132
|
+
parent_mod = Object.const_get(full_mod_name)
|
133
|
+
parent_mod.const_get(mod) && next rescue nil
|
134
|
+
parent_mod.const_set(mod, Module.new)
|
135
|
+
full_mod_name = "#{full_mod_name}::#{mod}"
|
136
|
+
end
|
137
|
+
Object.const_get(full_mod_name).const_set(mod_name, const)
|
138
|
+
end
|
94
139
|
end
|
95
140
|
end
|
96
141
|
end
|
data/lib/lightio/library/io.rb
CHANGED
@@ -6,118 +6,142 @@ module LightIO::Library
|
|
6
6
|
mock ::IO
|
7
7
|
extend LightIO::Module::IO::ClassMethods
|
8
8
|
|
9
|
-
|
9
|
+
def to_io
|
10
|
+
self
|
11
|
+
end
|
10
12
|
|
11
|
-
|
13
|
+
# abstract for io-like operations
|
14
|
+
module IOMethods
|
15
|
+
class << self
|
16
|
+
def included(base)
|
17
|
+
base.send(:wrap_blocking_methods, :read, :write)
|
18
|
+
base.send(:alias_method, :<<, :write)
|
19
|
+
end
|
20
|
+
end
|
12
21
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
def wait(timeout = nil, mode = :read)
|
23
|
+
io_watcher.wait(timeout, mode) && self
|
24
|
+
end
|
25
|
+
|
26
|
+
def wait_readable(timeout = nil)
|
27
|
+
wait(timeout, :read) && self
|
28
|
+
end
|
29
|
+
|
30
|
+
def wait_writable(timeout = nil)
|
31
|
+
wait(timeout, :write) && self
|
32
|
+
end
|
33
|
+
|
34
|
+
def read(length=nil, outbuf=nil)
|
35
|
+
raise ArgumentError, "negative length #{length} given" if length && length < 0
|
36
|
+
(outbuf ||= "").clear
|
37
|
+
loop do
|
38
|
+
readlen = length.nil? ? 4096 : length - outbuf.size
|
39
|
+
if (data = wait_nonblock(:read_nonblock, readlen))
|
40
|
+
outbuf << data
|
41
|
+
if length == outbuf.size
|
42
|
+
return outbuf
|
43
|
+
end
|
44
|
+
else
|
45
|
+
return length.nil? ? outbuf : nil
|
22
46
|
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def readpartial(maxlen, outbuf=nil)
|
51
|
+
(outbuf ||= "").clear
|
52
|
+
if (data = wait_nonblock(:read_nonblock, maxlen))
|
53
|
+
outbuf << data
|
23
54
|
else
|
24
|
-
|
55
|
+
raise EOFError, 'end of file reached'
|
25
56
|
end
|
57
|
+
outbuf
|
26
58
|
end
|
27
|
-
end
|
28
59
|
|
29
|
-
|
30
|
-
|
31
|
-
if (data = wait_nonblock(:read_nonblock, maxlen))
|
32
|
-
outbuf << data
|
33
|
-
else
|
34
|
-
raise EOFError, 'end of file reached'
|
60
|
+
def getbyte
|
61
|
+
read(1)
|
35
62
|
end
|
36
|
-
outbuf
|
37
|
-
end
|
38
63
|
|
39
|
-
|
40
|
-
|
41
|
-
|
64
|
+
def getc
|
65
|
+
wait_readable
|
66
|
+
@obj.getc
|
67
|
+
end
|
42
68
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
69
|
+
def readline(*args)
|
70
|
+
line = gets(*args)
|
71
|
+
raise EOFError, 'end of file reached' if line.nil?
|
72
|
+
line
|
73
|
+
end
|
47
74
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
75
|
+
def readlines(*args)
|
76
|
+
result = []
|
77
|
+
until eof?
|
78
|
+
result << readline(*args)
|
79
|
+
end
|
80
|
+
result
|
81
|
+
end
|
53
82
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
83
|
+
def readchar
|
84
|
+
c = getc
|
85
|
+
raise EOFError, 'end of file reached' if c.nil?
|
86
|
+
c
|
58
87
|
end
|
59
|
-
result
|
60
|
-
end
|
61
88
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
89
|
+
def readbyte
|
90
|
+
b = getbyte
|
91
|
+
raise EOFError, 'end of file reached' if b.nil?
|
92
|
+
b
|
93
|
+
end
|
67
94
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
95
|
+
def eof
|
96
|
+
wait_readable
|
97
|
+
@obj.eof
|
98
|
+
end
|
73
99
|
|
74
|
-
|
75
|
-
wait_readable
|
76
|
-
@obj.eof
|
77
|
-
end
|
100
|
+
alias eof? eof
|
78
101
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
$_ = s
|
98
|
-
end
|
102
|
+
def gets(*args)
|
103
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2
|
104
|
+
sep = $/
|
105
|
+
if args[0].is_a?(Numeric)
|
106
|
+
limit = args.shift
|
107
|
+
else
|
108
|
+
sep = args.shift if args.size > 0
|
109
|
+
limit = args.shift if args.first.is_a?(Numeric)
|
110
|
+
end
|
111
|
+
s = ''
|
112
|
+
while (c = getbyte)
|
113
|
+
s << c
|
114
|
+
break if limit && s.size == limit
|
115
|
+
break if c == sep
|
116
|
+
end
|
117
|
+
s = nil if s.empty?
|
118
|
+
$_ = s
|
119
|
+
end
|
99
120
|
|
100
|
-
|
101
|
-
|
102
|
-
|
121
|
+
def print(*obj)
|
122
|
+
obj.each do |s|
|
123
|
+
write(s)
|
124
|
+
end
|
125
|
+
end
|
103
126
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
127
|
+
def printf(*args)
|
128
|
+
write(sprintf(*args))
|
129
|
+
end
|
130
|
+
|
131
|
+
def puts(*obj)
|
132
|
+
obj.each do |s|
|
133
|
+
write(s)
|
134
|
+
write($/)
|
135
|
+
end
|
136
|
+
end
|
109
137
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
b = getbyte
|
116
|
-
if b
|
117
|
-
ungetbyte(b)
|
118
|
-
return
|
119
|
-
end
|
120
|
-
io_watcher.wait_readable
|
138
|
+
def close(*args)
|
139
|
+
# close watcher before io closed
|
140
|
+
io_watcher.close
|
141
|
+
@obj.close
|
142
|
+
end
|
121
143
|
end
|
144
|
+
|
145
|
+
prepend IOMethods
|
122
146
|
end
|
123
147
|
end
|
@@ -1,5 +1,12 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'forwardable'
|
1
3
|
module LightIO::Library
|
2
4
|
module KernelExt
|
5
|
+
KERNEL_PROXY = ::LightIO::RawProxy.new(::Kernel,
|
6
|
+
methods: [:spawn, :`])
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
|
3
10
|
def sleep(*duration)
|
4
11
|
if duration.size > 1
|
5
12
|
raise ArgumentError, "wrong number of arguments (given #{duration.size}, expected 0..1)"
|
@@ -14,5 +21,60 @@ module LightIO::Library
|
|
14
21
|
timer = LightIO::Watchers::Timer.new duration
|
15
22
|
LightIO::IOloop.current.wait(timer)
|
16
23
|
end
|
24
|
+
|
25
|
+
def spawn(*commands, **options)
|
26
|
+
options = options.dup
|
27
|
+
options.each do |key, v|
|
28
|
+
if key.is_a?(LightIO::Library::IO)
|
29
|
+
options.delete(key)
|
30
|
+
key = convert_io_or_array_to_raw(key)
|
31
|
+
options[key] = v
|
32
|
+
end
|
33
|
+
if (io = convert_io_or_array_to_raw(v))
|
34
|
+
options[key] = io
|
35
|
+
end
|
36
|
+
end
|
37
|
+
KERNEL_PROXY.send(:spawn, *commands, **options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def `(cmd)
|
41
|
+
Open3.popen3(cmd, out: STDOUT, err: STDERR) do |stdin, stdout, stderr, wait_thr|
|
42
|
+
output = LightIO::Library::IO._wrap(stdout).read
|
43
|
+
KERNEL_PROXY.send(:`, "exit #{wait_thr.value.exitstatus}")
|
44
|
+
return output
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def system(*cmd, **opt)
|
49
|
+
Open3.popen3(*cmd, **opt) do |stdin, stdout, stderr, wait_thr|
|
50
|
+
return nil if LightIO::Library::IO._wrap(stderr).read.size > 0
|
51
|
+
return wait_thr.value.exitstatus == 0
|
52
|
+
end
|
53
|
+
rescue Errno::ENOENT
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def_delegators :stdin, :gets, :readline, :readlines
|
58
|
+
|
59
|
+
private
|
60
|
+
def convert_io_or_array_to_raw(io_or_array)
|
61
|
+
if io_or_array.is_a?(LightIO::Library::IO)
|
62
|
+
io_or_array.send(:light_io_raw_obj)
|
63
|
+
elsif io_or_array.is_a?(Array)
|
64
|
+
io_or_array.map {|io| convert_io_or_array_to_raw(io)}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def stdin
|
69
|
+
@stdin ||= LightIO::Library::IO._wrap($stdin)
|
70
|
+
end
|
71
|
+
|
72
|
+
def stdout
|
73
|
+
@stdin ||= LightIO::Library::IO._wrap($stdout)
|
74
|
+
end
|
75
|
+
|
76
|
+
def stderr
|
77
|
+
@stdin ||= LightIO::Library::IO._wrap($stderr)
|
78
|
+
end
|
17
79
|
end
|
18
80
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
module LightIO::Library
|
3
|
+
module OpenSSL
|
4
|
+
module SSL
|
5
|
+
class SSLSocket
|
6
|
+
include Base
|
7
|
+
include LightIO::Wrap::IOWrapper
|
8
|
+
|
9
|
+
mock ::OpenSSL::SSL::SSLSocket
|
10
|
+
prepend LightIO::Library::IO::IOMethods
|
11
|
+
|
12
|
+
wrap_blocking_methods :connect, :accept
|
13
|
+
|
14
|
+
def initialize(io, *args)
|
15
|
+
io = io.send(:light_io_raw_obj) if io.is_a?(LightIO::Library::IO)
|
16
|
+
super(io, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def accept_nonblock
|
20
|
+
socket = @obj.accept_nonblock(*args)
|
21
|
+
socket.is_a?(Symbol) ? socket : self.class._wrap(socket)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -3,11 +3,53 @@ require 'socket'
|
|
3
3
|
module LightIO::Library
|
4
4
|
|
5
5
|
class Addrinfo
|
6
|
-
include LightIO::Wrap::Wrapper
|
7
6
|
include Base
|
7
|
+
include LightIO::Wrap::Wrapper
|
8
|
+
|
8
9
|
mock ::Addrinfo
|
10
|
+
extend LightIO::Module::Addrinfo::ClassMethods
|
11
|
+
|
12
|
+
module WrapHelper
|
13
|
+
protected
|
14
|
+
def wrap_socket_method(method)
|
15
|
+
define_method method do |*args|
|
16
|
+
socket = self.class.wrap_to_library(@obj.send(method, *args))
|
17
|
+
if block_given?
|
18
|
+
begin
|
19
|
+
yield socket
|
20
|
+
ensure
|
21
|
+
socket.close
|
22
|
+
end
|
23
|
+
else
|
24
|
+
socket
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def wrap_socket_methods(*methods)
|
30
|
+
methods.each {|m| wrap_socket_method(m)}
|
31
|
+
end
|
32
|
+
|
33
|
+
def wrap_addrinfo_return_method(method)
|
34
|
+
define_method method do |*args|
|
35
|
+
result = @obj.send(method, *args)
|
36
|
+
if result.is_a?(::Addrinfo)
|
37
|
+
self.class.wrap_to_library(result)
|
38
|
+
elsif result.respond_to?(:map)
|
39
|
+
result.map {|r| self.class.wrap_to_library(r)}
|
40
|
+
else
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def wrap_addrinfo_return_methods(*methods)
|
47
|
+
methods.each {|m| wrap_addrinfo_return_method(m)}
|
48
|
+
end
|
49
|
+
end
|
9
50
|
|
10
|
-
|
51
|
+
include LightIO::Module::Base::Helper
|
52
|
+
extend WrapHelper
|
11
53
|
|
12
54
|
wrap_socket_methods :bind, :connect, :connect_from, :connect_to, :listen
|
13
55
|
wrap_addrinfo_return_methods :family_addrinfo, :ipv6_to_ipv4
|
@@ -96,12 +138,37 @@ module LightIO::Library
|
|
96
138
|
include Base
|
97
139
|
mock ::TCPSocket
|
98
140
|
wrap_methods_run_in_threads_pool :gethostbyname
|
141
|
+
|
142
|
+
def initialize(*args)
|
143
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 2..4)" if args.size < 2 || args.size > 4
|
144
|
+
host, port = args[0..1]
|
145
|
+
local_host, local_port = args[2..3]
|
146
|
+
addrinfo = Addrinfo.getaddrinfo(host, port, nil, :STREAM)[0]
|
147
|
+
socket = ::Socket.send(:origin_new, addrinfo.afamily, Socket::SOCK_STREAM, 0)
|
148
|
+
if local_host || local_port
|
149
|
+
local_address = Socket.sockaddr_in(local_port, local_host)
|
150
|
+
socket.bind(local_address)
|
151
|
+
end
|
152
|
+
remote_address = Socket.sockaddr_in(addrinfo.ip_port, addrinfo.ip_address)
|
153
|
+
@obj = socket
|
154
|
+
wait_nonblock(:connect_nonblock, remote_address)
|
155
|
+
@obj
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
def connect_nonblock(*args)
|
160
|
+
@obj.connect_nonblock(*args)
|
161
|
+
end
|
99
162
|
end
|
100
163
|
|
101
164
|
class TCPServer < TCPSocket
|
102
165
|
include Base
|
103
166
|
mock ::TCPServer
|
104
167
|
|
168
|
+
def initialize(*args)
|
169
|
+
@obj = ::TCPServer.send(:origin_new, *args)
|
170
|
+
end
|
171
|
+
|
105
172
|
def accept
|
106
173
|
socket = wait_nonblock(:accept_nonblock)
|
107
174
|
TCPSocket._wrap(socket)
|
@@ -125,12 +192,12 @@ module LightIO::Library
|
|
125
192
|
wrap_blocking_methods :recvfrom
|
126
193
|
end
|
127
194
|
|
128
|
-
class UNIXSocket <
|
195
|
+
class UNIXSocket < BasicSocket
|
129
196
|
include Base
|
130
197
|
mock ::UNIXSocket
|
131
198
|
|
132
199
|
def send_io(io)
|
133
|
-
io = io.
|
200
|
+
io = io.send(:light_io_raw_obj) if io.is_a?(LightIO::Library::IO)
|
134
201
|
@obj.send_io(io)
|
135
202
|
end
|
136
203
|
|
data/lib/lightio/module.rb
CHANGED
data/lib/lightio/module/base.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
module LightIO::Module
|
2
2
|
module Base
|
3
|
+
class << self
|
4
|
+
def find_library_class(klass)
|
5
|
+
return LightIO::Library::Base.send(:nameless_classes)[klass] if klass.name.nil?
|
6
|
+
name = klass.name
|
7
|
+
namespace_index = name.rindex("::")
|
8
|
+
class_name = namespace_index.nil? ? name : name[(namespace_index + 2)..-1]
|
9
|
+
LightIO::Library.const_get(class_name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
3
13
|
module NewHelper
|
4
14
|
protected
|
5
15
|
def define_new_for_modules(*mods)
|
@@ -28,12 +38,8 @@ module LightIO::Module
|
|
28
38
|
find_library_class._wrap(obj)
|
29
39
|
end
|
30
40
|
|
31
|
-
# private
|
32
41
|
def find_library_class
|
33
|
-
|
34
|
-
namespace_index = name.rindex("::")
|
35
|
-
class_name = namespace_index.nil? ? name : name[(namespace_index + 2)..-1]
|
36
|
-
LightIO::Library.const_get(class_name)
|
42
|
+
Base.find_library_class(self)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
@@ -10,44 +10,6 @@ module LightIO::Module
|
|
10
10
|
|
11
11
|
module WrapperHelper
|
12
12
|
protected
|
13
|
-
include LightIO::Module::Base::Helper
|
14
|
-
|
15
|
-
def wrap_socket_method(method)
|
16
|
-
define_method method do |*args|
|
17
|
-
socket = wrap_to_library(@obj.send(method, *args))
|
18
|
-
if block_given?
|
19
|
-
begin
|
20
|
-
yield socket
|
21
|
-
ensure
|
22
|
-
socket.close
|
23
|
-
end
|
24
|
-
else
|
25
|
-
socket
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def wrap_socket_methods(*methods)
|
31
|
-
methods.each {|m| wrap_socket_method(m)}
|
32
|
-
end
|
33
|
-
|
34
|
-
def wrap_addrinfo_return_method(method)
|
35
|
-
define_method method do |*args|
|
36
|
-
result = @obj.send(method, *args)
|
37
|
-
if result.is_a?(::Addrinfo)
|
38
|
-
wrap_to_library(result)
|
39
|
-
elsif result.respond_to?(:map)
|
40
|
-
result.map {|r| wrap_to_library(r)}
|
41
|
-
else
|
42
|
-
result
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def wrap_addrinfo_return_methods(*methods)
|
48
|
-
methods.each {|m| wrap_addrinfo_return_method(m)}
|
49
|
-
end
|
50
|
-
|
51
13
|
def wrap_class_addrinfo_return_method(method)
|
52
14
|
define_method method do |*args|
|
53
15
|
result = __send__(:"origin_#{method}", *args)
|
@@ -67,10 +29,11 @@ module LightIO::Module
|
|
67
29
|
end
|
68
30
|
|
69
31
|
module ClassMethods
|
32
|
+
include LightIO::Module::Base::Helper
|
70
33
|
extend WrapperHelper
|
71
34
|
|
72
35
|
def foreach(*args, &block)
|
73
|
-
Addrinfo.getaddrinfo(*args).each(&block)
|
36
|
+
LightIO::Library::Addrinfo.getaddrinfo(*args).each(&block)
|
74
37
|
end
|
75
38
|
|
76
39
|
wrap_class_addrinfo_return_methods :getaddrinfo, :ip, :udp, :tcp, :unix
|
data/lib/lightio/monkey.rb
CHANGED
@@ -3,7 +3,7 @@ module LightIO
|
|
3
3
|
class PatchError < StandardError
|
4
4
|
end
|
5
5
|
|
6
|
-
IO_PATCH_CONSTANTS = %w{IO File Socket Socket::Ifaddr TCPServer TCPSocket BasicSocket Addrinfo IPSocket UDPSocket UNIXSocket UNIXServer}.freeze
|
6
|
+
IO_PATCH_CONSTANTS = %w{IO File Socket Socket::Ifaddr TCPServer TCPSocket BasicSocket Addrinfo IPSocket UDPSocket UNIXSocket UNIXServer OpenSSL::SSL::SSLSocket}.freeze
|
7
7
|
THREAD_PATCH_CONSTANTS = %w{Thread ThreadGroup Queue SizedQueue ConditionVariable Mutex ThreadsWait}.freeze
|
8
8
|
|
9
9
|
@patched
|
@@ -13,12 +13,14 @@ module LightIO
|
|
13
13
|
patch_thread!
|
14
14
|
patch_io!
|
15
15
|
patch_kernel!
|
16
|
+
nil
|
16
17
|
end
|
17
18
|
|
18
19
|
def unpatch_all!
|
19
20
|
unpatch_thread!
|
20
21
|
unpatch_io!
|
21
22
|
unpatch_kernel!
|
23
|
+
nil
|
22
24
|
end
|
23
25
|
|
24
26
|
def patched?(obj)
|
@@ -29,36 +31,54 @@ module LightIO
|
|
29
31
|
require 'thread'
|
30
32
|
THREAD_PATCH_CONSTANTS.each {|klass_name| patch!(klass_name)}
|
31
33
|
patch_method!(Timeout, :timeout, LightIO::Timeout.method(:timeout))
|
34
|
+
nil
|
32
35
|
end
|
33
36
|
|
34
37
|
def unpatch_thread!
|
35
38
|
require 'thread'
|
36
39
|
THREAD_PATCH_CONSTANTS.each {|klass_name| unpatch!(klass_name)}
|
37
40
|
unpatch_method!(Timeout, :timeout)
|
41
|
+
nil
|
38
42
|
end
|
39
43
|
|
40
44
|
def patch_io!
|
41
45
|
require 'socket'
|
42
46
|
IO_PATCH_CONSTANTS.each {|klass_name| patch!(klass_name)}
|
47
|
+
patch_method!(Process, :spawn, LightIO.method(:spawn).to_proc)
|
48
|
+
nil
|
43
49
|
end
|
44
50
|
|
45
51
|
def unpatch_io!
|
46
52
|
require 'socket'
|
47
53
|
IO_PATCH_CONSTANTS.each {|klass_name| unpatch!(klass_name)}
|
54
|
+
unpatch_method!(Process, :spawn)
|
55
|
+
nil
|
48
56
|
end
|
49
57
|
|
50
58
|
def patch_kernel!
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
patch_kernel_method!(:sleep, LightIO.method(:sleep))
|
60
|
+
patch_kernel_method!(:select, LightIO::Library::IO.method(:select))
|
61
|
+
patch_kernel_method!(:open, LightIO::Library::File.method(:open).to_proc)
|
62
|
+
patch_kernel_method!(:spawn, LightIO.method(:spawn).to_proc)
|
63
|
+
patch_kernel_method!(:`, LightIO.method(:`).to_proc)
|
64
|
+
patch_kernel_method!(:system, LightIO.method(:system).to_proc)
|
65
|
+
%w{gets readline readlines}.each do |method|
|
66
|
+
patch_kernel_method!(method.to_sym, LightIO.method(method.to_sym).to_proc)
|
67
|
+
end
|
68
|
+
nil
|
55
69
|
end
|
56
70
|
|
57
71
|
def unpatch_kernel!
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
72
|
+
unpatch_kernel_method!(:sleep)
|
73
|
+
unpatch_kernel_method!(:select)
|
74
|
+
unpatch_kernel_method!(:open)
|
75
|
+
unpatch_kernel_method!(:spawn)
|
76
|
+
unpatch_kernel_method!(:`)
|
77
|
+
unpatch_kernel_method!(:system)
|
78
|
+
%w{gets readline readlines}.each do |method|
|
79
|
+
unpatch_kernel_method!(method.to_sym, LightIO.method(method.to_sym).to_proc)
|
80
|
+
end
|
81
|
+
nil
|
62
82
|
end
|
63
83
|
|
64
84
|
private
|
@@ -88,6 +108,16 @@ module LightIO
|
|
88
108
|
patched.delete(klass)
|
89
109
|
end
|
90
110
|
|
111
|
+
def patch_kernel_method!(method_name, method)
|
112
|
+
patch_method!(Kernel, method_name, method)
|
113
|
+
patch_instance_method!(Kernel, method_name, method)
|
114
|
+
end
|
115
|
+
|
116
|
+
def unpatch_kernel_method!(method_name)
|
117
|
+
unpatch_method!(Kernel, method_name)
|
118
|
+
unpatch_instance_method!(Kernel, method_name)
|
119
|
+
end
|
120
|
+
|
91
121
|
def find_class_methods_module(klass_name)
|
92
122
|
LightIO::Module.const_get("#{klass_name}::ClassMethods", false)
|
93
123
|
rescue NameError
|
data/lib/lightio/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lightio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.0
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jiang Jinyang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -92,6 +92,7 @@ files:
|
|
92
92
|
- examples/beams.rb
|
93
93
|
- examples/echo_server.rb
|
94
94
|
- examples/echo_server_with_raw_socket.rb
|
95
|
+
- examples/http_requests.rb
|
95
96
|
- examples/monkey_patch.rb
|
96
97
|
- lib/lightio.rb
|
97
98
|
- lib/lightio/core.rb
|
@@ -106,6 +107,7 @@ files:
|
|
106
107
|
- lib/lightio/library/file.rb
|
107
108
|
- lib/lightio/library/io.rb
|
108
109
|
- lib/lightio/library/kernel_ext.rb
|
110
|
+
- lib/lightio/library/openssl.rb
|
109
111
|
- lib/lightio/library/queue.rb
|
110
112
|
- lib/lightio/library/sized_queue.rb
|
111
113
|
- lib/lightio/library/socket.rb
|
@@ -116,6 +118,7 @@ files:
|
|
116
118
|
- lib/lightio/module/base.rb
|
117
119
|
- lib/lightio/module/file.rb
|
118
120
|
- lib/lightio/module/io.rb
|
121
|
+
- lib/lightio/module/openssl.rb
|
119
122
|
- lib/lightio/module/socket.rb
|
120
123
|
- lib/lightio/module/thread.rb
|
121
124
|
- lib/lightio/module/threads_wait.rb
|
@@ -144,9 +147,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
147
|
version: 2.3.4
|
145
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
149
|
requirements:
|
147
|
-
- - "
|
150
|
+
- - ">="
|
148
151
|
- !ruby/object:Gem::Version
|
149
|
-
version:
|
152
|
+
version: '0'
|
150
153
|
requirements: []
|
151
154
|
rubyforge_project:
|
152
155
|
rubygems_version: 2.6.11
|