|
| 1 | +#!/usr/bin/env ruby |
| 2 | + |
| 3 | +# Use the locally built curb (extension + libs) instead of any installed gem. |
| 4 | +$LOAD_PATH.unshift File.expand_path('../lib', __dir__) |
| 5 | +$LOAD_PATH.unshift File.expand_path('../ext', __dir__) |
| 6 | + |
| 7 | +require 'curl' |
| 8 | + |
| 9 | +# Demo parameters |
| 10 | +GROUPS = (ENV['GROUPS'] || 3).to_i |
| 11 | +PER_GROUP = (ENV['PER_GROUP'] || 5).to_i |
| 12 | +CONNECT_TIMEOUT = (ENV['CONNECT_TIMEOUT'] || 5).to_i |
| 13 | +TOTAL_TIMEOUT = (ENV['TOTAL_TIMEOUT'] || 15).to_i |
| 14 | +DELAY_S = (ENV['DELAY_S'] || 0.25).to_f |
| 15 | + |
| 16 | +require 'webrick' |
| 17 | +require 'async' |
| 18 | + |
| 19 | +# Start a small local HTTP server on loopback and wait until it’s listening. |
| 20 | +ready = Queue.new |
| 21 | +server = WEBrick::HTTPServer.new( |
| 22 | + BindAddress: '127.0.0.1', |
| 23 | + Port: (ENV['PORT'] || 0).to_i, # 0 chooses a free port |
| 24 | + Logger: WEBrick::Log.new($stderr, WEBrick::Log::FATAL), |
| 25 | + AccessLog: [], |
| 26 | + StartCallback: -> { ready << true } |
| 27 | +) |
| 28 | +server.mount_proc('/test') do |_req, res| |
| 29 | + sleep DELAY_S |
| 30 | + res.status = 200 |
| 31 | + res['Content-Type'] = 'text/plain' |
| 32 | + res.body = 'OK' |
| 33 | +end |
| 34 | + |
| 35 | +server_thread = Thread.new { server.start } |
| 36 | +ready.pop # wait until the server is bound |
| 37 | +bound_port = server.listeners.first.addr[1] |
| 38 | +URL = "http://127.0.0.1:#{bound_port}/test" |
| 39 | + |
| 40 | +puts "Async demo: groups=#{GROUPS} per_group=#{PER_GROUP} url=#{URL} delay=#{DELAY_S}s" |
| 41 | + |
| 42 | +def crawl(url) |
| 43 | + c = Curl::Easy.new |
| 44 | + c.url = url |
| 45 | + c.connect_timeout = CONNECT_TIMEOUT |
| 46 | + c.timeout = TOTAL_TIMEOUT |
| 47 | + start = Time.now |
| 48 | + code = nil |
| 49 | + error = nil |
| 50 | + begin |
| 51 | + c.perform |
| 52 | + code = c.response_code |
| 53 | + rescue => e |
| 54 | + code = -1 |
| 55 | + error = "#{e.class}: #{e.message}" |
| 56 | + end |
| 57 | + { code: code, error: error, duration: (Time.now - start) } |
| 58 | +end |
| 59 | + |
| 60 | +results = [] |
| 61 | +started = Time.now |
| 62 | +if Async.respond_to?(:run) |
| 63 | + Async.run do |top| |
| 64 | + GROUPS.times do |
| 65 | + PER_GROUP.times do |
| 66 | + top.async do |
| 67 | + results << crawl(URL) |
| 68 | + end |
| 69 | + end |
| 70 | + end |
| 71 | + end |
| 72 | +else |
| 73 | + Async do |top| |
| 74 | + GROUPS.times do |
| 75 | + PER_GROUP.times do |
| 76 | + top.async do |
| 77 | + results << crawl(URL) |
| 78 | + end |
| 79 | + end |
| 80 | + end |
| 81 | + end |
| 82 | +end |
| 83 | +duration = Time.now - started |
| 84 | + |
| 85 | +ok = results.count { |r| r[:code] == 200 } |
| 86 | +fail = results.count { |r| r[:code] != 200 } |
| 87 | +puts "Completed #{results.size} requests in #{duration.round(3)}s (ok=#{ok}, fail=#{fail})" |
| 88 | + |
| 89 | +server.shutdown |
| 90 | +server_thread.join |
0 commit comments