async-await 0.5.0 → 0.6.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
  SHA256:
3
- metadata.gz: 982110e1afe4ee24f5931671df9df8036170b4b2b3cb4dcc8bc1e924739c2099
4
- data.tar.gz: d6fd5e852edadf8a2b48090847225ac1df2e372ac37c4fbd126b0dc6970c9637
3
+ metadata.gz: 60b68ca501e24b540ab2868d13331718fedce992928ed6ac3e29ae00caa3ef82
4
+ data.tar.gz: 1e56657845f348d4c5657974a35c417176ddf5e63c595a8084e3fe0f7ff2f291
5
5
  SHA512:
6
- metadata.gz: f05e45a97e6cac339a4f6a1bba1e59d2a78ebe7f8f0741adb95525f9c376b5c358685d7a3b68ef3df98ece474f269903fd205bc74d7002b0c6a77183f0cd943f
7
- data.tar.gz: 0c43a301fb491a90611d14717885913c6934c943f2616d744e23e360bc86b43cb33cd6752dddd7edbcbff370220bbd916f23fa994d302d938f9b9f7719921e89
6
+ metadata.gz: 502521efb2e174c861cc324fbdb8c141e37c16ddd2c91664fd54e3b90d9e4a3bc103ef12a01b41deb4fed4d3997a2bce4820b14d9d10f3ea38f50b4b0b505810
7
+ data.tar.gz: 231a2b125fa5f63260ea6cfb83f06c7beab995e35c868b7d87932b4105d43b31491287081d431ecdee9ef9a906d4b051dcf3c79a49d90826511fef2d22561b15
@@ -22,17 +22,17 @@ module Async
22
22
  module Await
23
23
  module Enumerable
24
24
  def async_map(parent: Task.current, &block)
25
- self.map do |*args|
25
+ self.map do |*arguments|
26
26
  parent.async do
27
- yield *args
27
+ yield(*arguments)
28
28
  end
29
29
  end.map(&:wait)
30
30
  end
31
31
 
32
32
  def async_each(parent: Task.current, &block)
33
- self.each do |*args|
33
+ self.each do |*arguments|
34
34
  parent.async do
35
- yield *args
35
+ yield(*arguments)
36
36
  end
37
37
  end
38
38
 
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module Await
23
- VERSION = "0.5.0"
23
+ VERSION = "0.6.0"
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-await
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-09 00:00:00.000000000 Z
11
+ date: 2021-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.3'
27
- - !ruby/object:Gem::Dependency
28
- name: async-rspec
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.1'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
24
+ - - ">="
39
25
  - !ruby/object:Gem::Version
40
- version: '1.1'
26
+ version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: ruby2_keywords
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -53,21 +39,21 @@ dependencies:
53
39
  - !ruby/object:Gem::Version
54
40
  version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: covered
42
+ name: async-rspec
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - ">="
45
+ - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '0'
47
+ version: '1.1'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - ">="
52
+ - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '0'
54
+ version: '1.1'
69
55
  - !ruby/object:Gem::Dependency
70
- name: bundler
56
+ name: covered
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
59
  - - ">="
@@ -80,20 +66,6 @@ dependencies:
80
66
  - - ">="
81
67
  - !ruby/object:Gem::Version
82
68
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '10.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '10.0'
97
69
  - !ruby/object:Gem::Dependency
98
70
  name: rspec
99
71
  requirement: !ruby/object:Gem::Requirement
@@ -108,36 +80,21 @@ dependencies:
108
80
  - - "~>"
109
81
  - !ruby/object:Gem::Version
110
82
  version: '3.0'
111
- description:
83
+ description:
112
84
  email:
113
- - samuel.williams@oriontransfer.co.nz
114
85
  executables: []
115
86
  extensions: []
116
87
  extra_rdoc_files: []
117
88
  files:
118
- - ".editorconfig"
119
- - ".gitignore"
120
- - ".rspec"
121
- - ".travis.yml"
122
- - Gemfile
123
- - README.md
124
- - Rakefile
125
- - async-await.gemspec
126
- - examples/chickens.rb
127
- - examples/echo.rb
128
- - examples/port_scanner/README.md
129
- - examples/port_scanner/port_scanner.go
130
- - examples/port_scanner/port_scanner.py
131
- - examples/port_scanner/port_scanner.rb
132
- - examples/sleep_sort.rb
133
89
  - lib/async/await.rb
134
90
  - lib/async/await/enumerable.rb
135
91
  - lib/async/await/methods.rb
136
92
  - lib/async/await/version.rb
137
93
  homepage: https://github.com/socketry/async-await
138
- licenses: []
94
+ licenses:
95
+ - MIT
139
96
  metadata: {}
140
- post_install_message:
97
+ post_install_message:
141
98
  rdoc_options: []
142
99
  require_paths:
143
100
  - lib
@@ -152,8 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
109
  - !ruby/object:Gem::Version
153
110
  version: '0'
154
111
  requirements: []
155
- rubygems_version: 3.1.2
156
- signing_key:
112
+ rubygems_version: 3.3.0
113
+ signing_key:
157
114
  specification_version: 4
158
115
  summary: Implements the async/await pattern on top of async :)
159
116
  test_files: []
data/.editorconfig DELETED
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
-
11
- # rspec failure tracking
12
- .rspec_status
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper
data/.travis.yml DELETED
@@ -1,19 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
-
4
- matrix:
5
- include:
6
- - rvm: 2.3
7
- - rvm: 2.4
8
- - rvm: 2.5
9
- - rvm: 2.6
10
- - rvm: 2.7
11
- - rvm: 2.6
12
- env: COVERAGE=BriefSummary,Coveralls
13
- - rvm: ruby-head
14
- - rvm: jruby-head
15
- - rvm: truffleruby
16
- allow_failures:
17
- - rvm: ruby-head
18
- - rvm: jruby-head
19
- - rvm: truffleruby
data/Gemfile DELETED
@@ -1,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :development do
6
- gem 'pry'
7
- gem 'async-io', '~> 1.4'
8
- end
9
-
10
- group :test do
11
- gem 'benchmark-ips'
12
- gem 'ruby-prof', platforms: :mri
13
- end
data/README.md DELETED
@@ -1,95 +0,0 @@
1
- # Async::Await
2
-
3
- Implements the async/await pattern for Ruby using [async].
4
-
5
- [![Build Status](https://secure.travis-ci.org/socketry/async-await.svg)](http://travis-ci.org/socketry/async-await)
6
- [![Code Climate](https://codeclimate.com/github/socketry/async-await.svg)](https://codeclimate.com/github/socketry/async-await)
7
- [![Coverage Status](https://coveralls.io/repos/socketry/async-await/badge.svg)](https://coveralls.io/r/socketry/async-await)
8
-
9
- [async]: https://github.com/socketry/async
10
-
11
- ## Installation
12
-
13
- Add this line to your application's Gemfile:
14
-
15
- ```ruby
16
- gem 'async-await'
17
- ```
18
-
19
- And then execute:
20
-
21
- $ bundle
22
-
23
- Or install it yourself as:
24
-
25
- $ gem install async-await
26
-
27
- ## Usage
28
-
29
- In any asynchronous context (e.g. a reactor), simply use the `await` function like so:
30
-
31
- ```ruby
32
- require 'async/await'
33
-
34
- class Coop
35
- include Async::Await
36
-
37
- async def count_chickens(area_name)
38
- 3.times do |i|
39
- sleep rand
40
-
41
- puts "Found a chicken in the #{area_name}!"
42
- end
43
- end
44
-
45
- async def count_all_chickens
46
- # These methods all run at the same time.
47
- count_chickens("garden")
48
- count_chickens("house")
49
-
50
- # We wait for the result
51
- count_chickens("tree").wait
52
- end
53
- end
54
-
55
- coop = Coop.new
56
- coop.count_all_chickens
57
- ```
58
-
59
- ## Contributing
60
-
61
- 1. Fork it
62
- 2. Create your feature branch (`git checkout -b my-new-feature`)
63
- 3. Commit your changes (`git commit -am 'Add some feature'`)
64
- 4. Push to the branch (`git push origin my-new-feature`)
65
- 5. Create new Pull Request
66
-
67
- ## See Also
68
-
69
- - [async-io](https://github.com/socketry/async-io) — Asynchronous networking and sockets.
70
- - [async-dns](https://github.com/socketry/async-dns) — Asynchronous DNS resolver and server.
71
- - [async-rspec](https://github.com/socketry/async-rspec) — Shared contexts for running async specs.
72
-
73
- ## License
74
-
75
- Released under the MIT license.
76
-
77
- Copyright, 2017, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
78
-
79
- Permission is hereby granted, free of charge, to any person obtaining a copy
80
- of this software and associated documentation files (the "Software"), to deal
81
- in the Software without restriction, including without limitation the rights
82
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
83
- copies of the Software, and to permit persons to whom the Software is
84
- furnished to do so, subject to the following conditions:
85
-
86
- The above copyright notice and this permission notice shall be included in
87
- all copies or substantial portions of the Software.
88
-
89
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
90
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
91
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
92
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
93
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
94
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
95
- THE SOFTWARE.
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/async-await.gemspec DELETED
@@ -1,28 +0,0 @@
1
- # coding: utf-8
2
- require_relative "lib/async/await/version"
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "async-await"
6
- spec.version = Async::Await::VERSION
7
- spec.authors = ["Samuel Williams"]
8
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
-
10
- spec.summary = "Implements the async/await pattern on top of async :)"
11
- spec.homepage = "https://github.com/socketry/async-await"
12
-
13
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
14
- f.match(%r{^(test|spec|features)/})
15
- end
16
-
17
- spec.require_paths = ["lib"]
18
-
19
- spec.add_dependency "async", "~> 1.3"
20
- spec.add_development_dependency "async-rspec", "~> 1.1"
21
-
22
- spec.add_dependency "ruby2_keywords"
23
-
24
- spec.add_development_dependency "covered"
25
- spec.add_development_dependency "bundler"
26
- spec.add_development_dependency "rake", "~> 10.0"
27
- spec.add_development_dependency "rspec", "~> 3.0"
28
- end
data/examples/chickens.rb DELETED
@@ -1,37 +0,0 @@
1
-
2
- require_relative '../lib/async/await'
3
-
4
- class Coop
5
- include Async::Await
6
-
7
- async def count_chickens(area_name)
8
- 3.times do |i|
9
- sleep rand
10
-
11
- puts "Found a chicken in the #{area_name}!"
12
- end
13
- end
14
-
15
- async def find_chicken(areas)
16
- puts "Searching for chicken..."
17
-
18
- sleep rand * 5
19
-
20
- return areas.sample
21
- end
22
-
23
- async def count_all_chickens
24
- # These methods all run at the same time.
25
- count_chickens("garden")
26
- count_chickens("house")
27
- count_chickens("tree")
28
-
29
- # Wait for all previous async work to complete...
30
- barrier!
31
-
32
- puts "There was a chicken in the #{find_chicken(["garden", "house", "tree"]).wait}"
33
- end
34
- end
35
-
36
- coop = Coop.new
37
- coop.count_all_chickens
data/examples/echo.rb DELETED
@@ -1,55 +0,0 @@
1
-
2
- require_relative '../lib/async/await'
3
-
4
- require 'async/io'
5
- require 'async/io/tcp_socket'
6
-
7
- require 'pry'
8
-
9
- class Echo
10
- include Async::Await
11
- include Async::IO
12
-
13
- async def handle(peer, address)
14
- data = peer.gets
15
- peer.puts("#{data} #{Time.now}")
16
- ensure
17
- peer.close
18
- end
19
-
20
- async def server
21
- puts "Binding server..."
22
- server = TCPServer.new("127.0.0.1", 9009)
23
-
24
- handle(*server.accept)
25
- ensure
26
- server.close rescue nil
27
- end
28
-
29
- async def client
30
- puts "Client connecting..."
31
- client = TCPSocket.new("127.0.0.1", 9009)
32
-
33
- client.puts("Hello World!")
34
- response = client.gets
35
-
36
- puts "Server said: #{response}"
37
- ensure
38
- client.close rescue nil
39
- end
40
-
41
- async def run
42
- puts "Creating server..."
43
- server
44
-
45
- puts "Creating client..."
46
- client
47
-
48
- puts "Run returning..."
49
- end
50
- end
51
-
52
- puts "Starting echo..."
53
- echo = Echo.new
54
- echo.run
55
- puts "Echo finished :)"
@@ -1,90 +0,0 @@
1
- # Port Scanner
2
-
3
- A simple `connect`-based port scanner. It scans locahost for all open ports.
4
-
5
- ## Usage
6
-
7
- ### Go
8
-
9
- Go is pretty awesome, because when the operation would not block, it runs sequentially in the same thread. Go spins up threads and delegates work across available CPU cores.
10
-
11
- $ go get golang.org/x/sync/semaphore
12
- $ go build port_scanner.go
13
- $ time ./port_scanner
14
- 22 open
15
- 139 open
16
- 445 open
17
- 3306 open
18
- 5355 open
19
- 5432 open
20
- 6379 open
21
- 9293 open
22
- 9292 open
23
- 9516 open
24
- 9515 open
25
- 12046 open
26
- 12813 open
27
- ./port_scanner 1.70s user 1.18s system 503% cpu 0.572 total
28
-
29
- ### Python
30
-
31
- Python was the slowest. This is possibly due to the implementation of semaphore. It creates all 65,535 tasks, and then most of them block on the semaphore.
32
-
33
- $ ./port_scanner.py
34
- 5355 open
35
- 5432 open
36
- 3306 open
37
- 39610 open
38
- 58260 open
39
- 12813 open
40
- 139 open
41
- 445 open
42
- 12046 open
43
- 22 open
44
- 9292 open
45
- 9293 open
46
- 9515 open
47
- 9516 open
48
- 6379 open
49
- ./port_scanner.py 11.41s user 0.88s system 98% cpu 12.485 total
50
-
51
- ### Ruby
52
-
53
- Ruby performance isn't that bad. It's only about half as fast as Go, considering that Go runs across all cores, while the Ruby implementation is limited to one core.
54
-
55
- $ ./port_scanner.rb
56
- 22 open
57
- 139 open
58
- 445 open
59
- 3306 open
60
- 5432 open
61
- 5355 open
62
- 6379 open
63
- 9516 open
64
- 9515 open
65
- 9293 open
66
- 9292 open
67
- 12046 open
68
- 12813 open
69
- ./port_scanner.rb 5.99s user 1.18s system 95% cpu 7.543 total
70
-
71
- ## Notes
72
-
73
- ### Why do I sometimes see high ports?
74
-
75
- Believe it or not, you can connect to your own sockets.
76
-
77
- ```ruby
78
- require 'socket'
79
- a = Addrinfo.tcp("127.0.0.1", 50000)
80
- s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
81
- s.bind(a)
82
- s.connect(a)
83
-
84
- s.write("Hello World")
85
- => 11
86
- [8] pry(main)> s.read(11)
87
- => "Hello World"
88
- ```
89
-
90
- What's happening is that your socket is implicitly binding to a high port, and at the same time it's trying to connect to it.
@@ -1,107 +0,0 @@
1
- package main
2
-
3
- import (
4
- "context"
5
- "fmt"
6
- "golang.org/x/sync/semaphore"
7
- "net"
8
- "syscall"
9
- "strings"
10
- "sync"
11
- "time"
12
- )
13
-
14
- // The star of the show, of modest means,
15
- // used to manage the port scan for a single host.
16
- type PortScanner struct {
17
- ip string
18
- lock *semaphore.Weighted
19
- }
20
-
21
- // Provides a simple wrapper to initializing a PortScanner.
22
- func NewPortScanner(ip string, limit uint64) *PortScanner {
23
- return &PortScanner{
24
- ip: ip,
25
- lock: semaphore.NewWeighted(int64(limit)),
26
- }
27
- }
28
-
29
- // Compute the maximum number of files we can open.
30
- func FileLimit(max uint64) uint64 {
31
- var rlimit syscall.Rlimit
32
- err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
33
- if err != nil {
34
- panic(err)
35
- }
36
-
37
- if (max < rlimit.Cur) {
38
- return max
39
- }
40
-
41
- return rlimit.Cur
42
- }
43
-
44
- // As the name might suggest, this function checks if a given port
45
- // is open to TCP communication. Used in conjunction with the Start function
46
- // to sweep over a range of ports concurrently.
47
- func checkPortOpen(ip string, port int, timeout time.Duration) {
48
- conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
49
-
50
- if err != nil {
51
- if strings.Contains(err.Error(), "timeout") {
52
- fmt.Println(port, "timeout", err.Error())
53
- } else if strings.Contains(err.Error(), "deadline exceeded") {
54
- fmt.Println(port, "timeout", err.Error())
55
- } else if strings.Contains(err.Error(), "refused") {
56
- // fmt.Println(port, "closed", err.Error())
57
- } else {
58
- panic(err)
59
- }
60
- return
61
- }
62
-
63
- fmt.Println(port, "open")
64
- conn.Close()
65
- }
66
-
67
- // This function is the bread and butter of this script. It manages the
68
- // port scanning for a given range of ports with the given timeout value
69
- // to deal with filtered ports by a firewall typically.
70
- func (ps *PortScanner) Start(start, stop int, timeout time.Duration) {
71
- wg := sync.WaitGroup{}
72
- defer wg.Wait()
73
-
74
- for port := start; port <= stop; port++ {
75
-
76
- ctx := context.TODO()
77
-
78
- for {
79
- err := ps.lock.Acquire(ctx, 1)
80
- if err == nil {
81
- break
82
- }
83
- }
84
-
85
- wg.Add(1)
86
-
87
- go func(ip string, port int) {
88
- defer ps.lock.Release(1)
89
- defer wg.Done()
90
-
91
- checkPortOpen(ps.ip, port, timeout)
92
- }(ps.ip, port)
93
-
94
- }
95
- }
96
-
97
- // This function kicks off the whole shindig' and provides a
98
- // basic example of the internal API usage.
99
- func main() {
100
- batch_size := FileLimit(512)
101
-
102
- // Create a new PortScanner for localhost.
103
- ps := NewPortScanner("127.0.0.1", batch_size)
104
-
105
- // Start scanning all the ports on localhost.
106
- ps.Start(1, 65535, 1000*time.Millisecond)
107
- }
@@ -1,36 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- import os, resource
4
- import asyncio
5
-
6
- class PortScanner:
7
- def __init__(self, host="0.0.0.0", ports=range(1, 1024+1), batch_size=1024):
8
- self.host = host
9
- self.ports = ports
10
- self.semaphore = asyncio.Semaphore(value=batch_size)
11
- self.loop = asyncio.get_event_loop()
12
-
13
- async def scan_port(self, port, timeout):
14
- async with self.semaphore:
15
- try:
16
- future = asyncio.open_connection(self.host, port, loop=self.loop)
17
- reader, writer = await asyncio.wait_for(future, timeout=timeout)
18
- print("{} open".format(port))
19
- writer.close()
20
- except ConnectionRefusedError:
21
- pass
22
- # print("{} closed".format(port))
23
- except asyncio.TimeoutError:
24
- print("{} timeout".format(port))
25
-
26
- def start(self, timeout=1.0):
27
- self.loop.run_until_complete(asyncio.gather(
28
- *[self.scan_port(port, timeout) for port in self.ports]
29
- ))
30
-
31
- limits = resource.getrlimit(resource.RLIMIT_NOFILE)
32
- batch_size = min(512, limits[0])
33
-
34
- scanner = PortScanner(host="127.0.0.1", ports=range(1, 65535+1), batch_size=batch_size)
35
-
36
- scanner.start()
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'async/io'
4
- require 'async/semaphore'
5
- require_relative '../../lib/async/await'
6
-
7
- class PortScanner
8
- include Async::Await
9
- include Async::IO
10
-
11
- def initialize(host: '0.0.0.0', ports:, batch_size: 1024)
12
- @host = host
13
- @ports = ports
14
- @semaphore = Async::Semaphore.new(batch_size)
15
- end
16
-
17
- def scan_port(port, timeout)
18
- with_timeout(timeout) do
19
- address = Async::IO::Address.tcp(@host, port)
20
- peer = Socket.connect(address)
21
- puts "#{port} open"
22
- peer.close
23
- end
24
- rescue Errno::ECONNREFUSED
25
- # puts "#{port} closed"
26
- rescue Async::TimeoutError
27
- puts "#{port} timeout"
28
- end
29
-
30
- async def start(timeout = 1.0)
31
- @ports.map do |port|
32
- @semaphore.async do
33
- scan_port(port, timeout)
34
- end
35
- end.collect(&:result)
36
- end
37
- end
38
-
39
- limits = Process.getrlimit(Process::RLIMIT_NOFILE)
40
- batch_size = [512, (limits.first * 0.9).ceil].min
41
-
42
- scanner = PortScanner.new(host: "127.0.0.1", ports: Range.new(1, 65535), batch_size: batch_size)
43
-
44
- scanner.start
@@ -1,29 +0,0 @@
1
-
2
- require_relative '../lib/async/await'
3
-
4
- class << self
5
- include Async::Await
6
-
7
- async def sort_one(item, into)
8
- sleep(item.to_f)
9
- into << item
10
-
11
- puts "I've sorted #{item} for you."
12
- end
13
-
14
- async def sort(items)
15
- result = []
16
-
17
- items.each do |item|
18
- sort_one(item, result)
19
- end
20
-
21
- # Wait until all previous async method calls have finished executing.
22
- barrier!
23
-
24
- return result
25
- end
26
- end
27
-
28
- puts "Hold on, sorting..."
29
- puts sort([5, 2, 3, 4, 9, 2, 5, 7, 8]).result.inspect