lightio 0.4.0.pre → 0.4.0

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
  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