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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a88720041be21bfca13e5e2f38e63218661e81b1
4
- data.tar.gz: 280863b2d6dee03a5f433c560137cf3a7f21cd7e
3
+ metadata.gz: 7bce34bd364d03347bca1a76cceede8fbad2838a
4
+ data.tar.gz: f99f66ed5d275bf5cd2e214faf131a5a391abb88
5
5
  SHA512:
6
- metadata.gz: 9faf2d2f8cdbf5a54b012d534c059858c98425054a9698ed3ea93dd1a57947d3e1fc169e1acaf00ffed983b713b34e308b550681c08f939554f0294cf90c0718
7
- data.tar.gz: 4a596272e41a00d3bd879b5fa9a4532f05a191a834df4ed187a6557becbb7a25e02cbce94fff1cf4cdf52b8e16f4436d44e6f11129b78756476b9a9fef779a83
6
+ metadata.gz: 61e90a11df62adf44a9e28f380a4083671b086830474a2f142622d74e8d2c2925cdfdd0941487589cf8206f98b218521d1907acb0e6f730235574695e5fd52c1
7
+ data.tar.gz: 2d8d16a78c200c491bd1ab4eba245e10888354aab0a2bd04bd00c220a8b780b87e6bd70b870c92d5f31fac14bc3f6f9e889dc5a4cf3aca946ecb8a2c9dbf8ec3
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
- --format documentation
1
+ --format progress
2
2
  --color
3
3
  --require spec_helper
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lightio (0.4.0.pre)
4
+ lightio (0.4.0)
5
5
  nio4r (~> 2.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -6,11 +6,37 @@
6
6
  [![Coverage Status](https://coveralls.io/repos/github/socketry/lightio/badge.svg?branch=master)](https://coveralls.io/github/socketry/lightio?branch=master)
7
7
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jjyr/lightio/blob/master/LICENSE.txt)
8
8
 
9
- LightIO is a ruby concurrency networking library, that combines ruby fiber and fast IO event loop.
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
@@ -3,6 +3,7 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:"spec:library") do |t|
5
5
  t.exclude_pattern = 'spec/**/monkey_spec.rb'
6
+ t.rspec_opts = "--tag ~skip_library"
6
7
  end
7
8
 
8
9
  RSpec::Core::RakeTask.new(:"spec:monkey_patch") do |t|
@@ -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
@@ -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 {parent.transfer}
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
 
@@ -6,6 +6,7 @@ require_relative 'library/timeout'
6
6
  require_relative 'library/io'
7
7
  require_relative 'library/file'
8
8
  require_relative 'library/socket'
9
+ require_relative 'library/openssl'
9
10
  require_relative 'library/thread'
10
11
  require_relative 'library/threads_wait'
11
12
 
@@ -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
@@ -6,118 +6,142 @@ module LightIO::Library
6
6
  mock ::IO
7
7
  extend LightIO::Module::IO::ClassMethods
8
8
 
9
- wrap_blocking_methods :read, :write
9
+ def to_io
10
+ self
11
+ end
10
12
 
11
- alias_method :<<, :write
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
- def read(length=nil, outbuf=nil)
14
- raise ArgumentError, "negative length #{length} given" if length && length < 0
15
- (outbuf ||= "").clear
16
- loop do
17
- readlen = length.nil? ? 4096 : length - outbuf.size
18
- if (data = wait_nonblock(:read_nonblock, readlen))
19
- outbuf << data
20
- if length == outbuf.size
21
- return outbuf
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
- return length.nil? ? outbuf : nil
55
+ raise EOFError, 'end of file reached'
25
56
  end
57
+ outbuf
26
58
  end
27
- end
28
59
 
29
- def readpartial(maxlen, outbuf=nil)
30
- (outbuf ||= "").clear
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
- def getbyte
40
- read(1)
41
- end
64
+ def getc
65
+ wait_readable
66
+ @obj.getc
67
+ end
42
68
 
43
- def getc
44
- wait_readable
45
- @obj.getc
46
- end
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
- def readline(*args)
49
- line = gets(*args)
50
- raise EOFError, 'end of file reached' if line.nil?
51
- line
52
- end
75
+ def readlines(*args)
76
+ result = []
77
+ until eof?
78
+ result << readline(*args)
79
+ end
80
+ result
81
+ end
53
82
 
54
- def readlines(*args)
55
- result = []
56
- until eof?
57
- result << readline(*args)
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
- def readchar
63
- c = getc
64
- raise EOFError, 'end of file reached' if c.nil?
65
- c
66
- end
89
+ def readbyte
90
+ b = getbyte
91
+ raise EOFError, 'end of file reached' if b.nil?
92
+ b
93
+ end
67
94
 
68
- def readbyte
69
- b = getbyte
70
- raise EOFError, 'end of file reached' if b.nil?
71
- b
72
- end
95
+ def eof
96
+ wait_readable
97
+ @obj.eof
98
+ end
73
99
 
74
- def eof
75
- wait_readable
76
- @obj.eof
77
- end
100
+ alias eof? eof
78
101
 
79
- alias eof? eof
80
-
81
- def gets(*args)
82
- raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2
83
- return nil if eof?
84
- sep = $/
85
- if args[0].is_a?(Numeric)
86
- limit = args.shift
87
- else
88
- sep = args.shift if args.size > 0
89
- limit = args.shift if args.first.is_a?(Numeric)
90
- end
91
- s = ''
92
- while (c = getc)
93
- s << c
94
- break if limit && s.size == limit
95
- break if c == sep
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
- def to_io
101
- self
102
- end
121
+ def print(*obj)
122
+ obj.each do |s|
123
+ write(s)
124
+ end
125
+ end
103
126
 
104
- def close(*args)
105
- # close watcher before io closed
106
- io_watcher.close
107
- @obj.close
108
- end
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
- private
111
- def wait_readable
112
- # if IO is already readable, continue wait_readable may block it forever
113
- # so use getbyte detect this situation
114
- # Maybe move getc and gets to thread pool is a good idea
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
- extend LightIO::Module::Addrinfo::WrapperHelper
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 < ::BasicSocket
195
+ class UNIXSocket < BasicSocket
129
196
  include Base
130
197
  mock ::UNIXSocket
131
198
 
132
199
  def send_io(io)
133
- io = io.instance_variable_get(:@obj) || 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
 
@@ -2,6 +2,7 @@ require_relative 'module/base'
2
2
  require_relative 'module/io'
3
3
  require_relative 'module/file'
4
4
  require_relative 'module/socket'
5
+ require_relative 'module/openssl'
5
6
  require_relative 'module/thread'
6
7
  require_relative 'module/threads_wait'
7
8
 
@@ -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
- name = self.name
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
@@ -0,0 +1,12 @@
1
+ module LightIO::Module
2
+ extend Base::NewHelper
3
+
4
+ module OpenSSL
5
+ module SSL
6
+ module SSLSocket
7
+ end
8
+ end
9
+ end
10
+
11
+ define_new_for_module 'OpenSSL::SSL::SSLSocket'
12
+ 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
@@ -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
- patch_method!(Kernel, :sleep, LightIO.method(:sleep))
52
- patch_method!(Kernel, :select, LightIO::Library::IO.method(:select))
53
- patch_instance_method!(Kernel, :sleep, LightIO.method(:sleep))
54
- patch_instance_method!(Kernel, :select, LightIO::Library::IO.method(:select))
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
- unpatch_method!(Kernel, :sleep, LightIO.method(:sleep))
59
- unpatch_method!(Kernel, :select, LightIO::Library::IO.method(:select))
60
- unpatch_instance_method!(Kernel, :sleep, LightIO.method(:sleep))
61
- unpatch_instance_method!(Kernel, :select, LightIO::Library::IO.method(:select))
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
@@ -1,3 +1,3 @@
1
1
  module LightIO
2
- VERSION = "0.4.0.pre"
2
+ VERSION = "0.4.0"
3
3
  end
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.pre
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-01-30 00:00:00.000000000 Z
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: 1.3.1
152
+ version: '0'
150
153
  requirements: []
151
154
  rubyforge_project:
152
155
  rubygems_version: 2.6.11