async-http 0.52.2 → 0.53.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/async/http/body/delayed.rb +2 -2
- data/lib/async/http/body/hijack.rb +5 -0
- data/lib/async/http/body/pipe.rb +15 -7
- data/lib/async/http/body/slowloris.rb +2 -2
- data/lib/async/http/body/stream.rb +1 -1
- data/lib/async/http/client.rb +2 -2
- data/lib/async/http/endpoint.rb +2 -2
- data/lib/async/http/internet.rb +4 -0
- data/lib/async/http/protocol/http1/connection.rb +0 -5
- data/lib/async/http/protocol/http1/server.rb +4 -3
- data/lib/async/http/protocol/http2/output.rb +1 -1
- data/lib/async/http/protocol/http2/response.rb +5 -0
- data/lib/async/http/proxy.rb +24 -8
- data/lib/async/http/version.rb +1 -1
- metadata +22 -65
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -52
- data/.gitignore +0 -15
- data/.rspec +0 -3
- data/.travis.yml +0 -35
- data/README.md +0 -365
- data/async-http.gemspec +0 -39
- data/bake.rb +0 -0
- data/examples/compare/Gemfile +0 -9
- data/examples/compare/benchmark.rb +0 -78
- data/examples/download/chunked.rb +0 -86
- data/examples/fetch/Gemfile +0 -3
- data/examples/fetch/Gemfile.lock +0 -74
- data/examples/fetch/README.md +0 -3
- data/examples/fetch/config.ru +0 -28
- data/examples/fetch/public/index.html +0 -23
- data/examples/fetch/public/stream.js +0 -56
- data/examples/google/search.rb +0 -47
- data/examples/request.rb +0 -38
- data/examples/stream/stop.rb +0 -28
- data/examples/trenni/Gemfile +0 -5
- data/examples/trenni/streaming.rb +0 -35
- data/examples/upload/client.rb +0 -39
- data/examples/upload/data.txt +0 -41
- data/examples/upload/server.rb +0 -19
- data/examples/upload/upload.rb +0 -26
- data/gems.locked +0 -103
- data/gems.rb +0 -11
data/async-http.gemspec
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'lib/async/http/version'
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = "async-http"
|
6
|
-
spec.version = Async::HTTP::VERSION
|
7
|
-
spec.licenses = ["MIT"]
|
8
|
-
spec.authors = ["Samuel Williams"]
|
9
|
-
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
10
|
-
|
11
|
-
spec.summary = "A HTTP client and server library."
|
12
|
-
spec.homepage = "https://github.com/socketry/async-http"
|
13
|
-
|
14
|
-
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
15
|
-
f.match(%r{^(test|spec|features)/})
|
16
|
-
end
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f)}
|
18
|
-
spec.require_paths = ["lib"]
|
19
|
-
|
20
|
-
spec.add_dependency("async", "~> 1.25")
|
21
|
-
spec.add_dependency("async-io", "~> 1.28")
|
22
|
-
spec.add_dependency("async-pool", "~> 0.2")
|
23
|
-
|
24
|
-
spec.add_dependency("protocol-http", "~> 0.19.0")
|
25
|
-
spec.add_dependency("protocol-http1", "~> 0.13.0")
|
26
|
-
spec.add_dependency("protocol-http2", "~> 0.14.0")
|
27
|
-
|
28
|
-
# spec.add_dependency("openssl")
|
29
|
-
|
30
|
-
spec.add_development_dependency "async-rspec", "~> 1.10"
|
31
|
-
spec.add_development_dependency "async-container", "~> 0.14.0"
|
32
|
-
|
33
|
-
spec.add_development_dependency "rack-test"
|
34
|
-
|
35
|
-
spec.add_development_dependency "covered"
|
36
|
-
spec.add_development_dependency "bundler"
|
37
|
-
spec.add_development_dependency "bake-bundler"
|
38
|
-
spec.add_development_dependency "rspec", "~> 3.6"
|
39
|
-
end
|
data/bake.rb
DELETED
File without changes
|
data/examples/compare/Gemfile
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'benchmark'
|
4
|
-
|
5
|
-
require 'httpx'
|
6
|
-
|
7
|
-
require 'async'
|
8
|
-
require 'async/barrier'
|
9
|
-
require 'async/semaphore'
|
10
|
-
require 'async/http/internet'
|
11
|
-
|
12
|
-
URL = "https://www.codeotaku.com/index"
|
13
|
-
REPEATS = 10
|
14
|
-
|
15
|
-
Benchmark.bmbm do |x|
|
16
|
-
x.report("async-http") do
|
17
|
-
Async do
|
18
|
-
internet = Async::HTTP::Internet.new
|
19
|
-
|
20
|
-
i = 0
|
21
|
-
while i < REPEATS
|
22
|
-
response = internet.get(URL)
|
23
|
-
response.read
|
24
|
-
|
25
|
-
i += 1
|
26
|
-
end
|
27
|
-
ensure
|
28
|
-
internet&.close
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
x.report("async-http (pipelined)") do
|
33
|
-
Async do |task|
|
34
|
-
internet = Async::HTTP::Internet.new
|
35
|
-
semaphore = Async::Semaphore.new(100, parent: task)
|
36
|
-
barrier = Async::Barrier.new(parent: semaphore)
|
37
|
-
|
38
|
-
# Warm up the connection pool...
|
39
|
-
response = internet.get(URL)
|
40
|
-
response.read
|
41
|
-
|
42
|
-
i = 0
|
43
|
-
while i < REPEATS
|
44
|
-
barrier.async do
|
45
|
-
response = internet.get(URL)
|
46
|
-
|
47
|
-
response.read
|
48
|
-
end
|
49
|
-
|
50
|
-
i += 1
|
51
|
-
end
|
52
|
-
|
53
|
-
barrier.wait
|
54
|
-
ensure
|
55
|
-
internet&.close
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
x.report("httpx") do
|
60
|
-
i = 0
|
61
|
-
while i < REPEATS
|
62
|
-
response = HTTPX.get(URL)
|
63
|
-
|
64
|
-
response.read
|
65
|
-
|
66
|
-
i += 1
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
x.report("httpx (pipelined)") do
|
71
|
-
urls = [URL] * REPEATS
|
72
|
-
responses = HTTPX.get(*urls)
|
73
|
-
|
74
|
-
responses.each do |response|
|
75
|
-
response.read
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'async'
|
4
|
-
require 'async/clock'
|
5
|
-
require 'async/barrier'
|
6
|
-
require 'async/semaphore'
|
7
|
-
require_relative '../../lib/async/http/endpoint'
|
8
|
-
require_relative '../../lib/async/http/client'
|
9
|
-
|
10
|
-
Async do
|
11
|
-
url = "https://static.openfoodfacts.org/data/en.openfoodfacts.org.products.csv"
|
12
|
-
|
13
|
-
endpoint = Async::HTTP::Endpoint.parse(url)
|
14
|
-
client = Async::HTTP::Client.new(endpoint)
|
15
|
-
|
16
|
-
headers = {'user-agent' => 'curl/7.69.1', 'accept' => '*/*'}
|
17
|
-
|
18
|
-
file = File.open("products.csv", "w")
|
19
|
-
Async.logger.info(self) {"Saving download to #{Dir.pwd}"}
|
20
|
-
|
21
|
-
begin
|
22
|
-
response = client.head(endpoint.path, headers)
|
23
|
-
content_length = nil
|
24
|
-
|
25
|
-
if response.success?
|
26
|
-
unless response.headers['accept-ranges'].include?('bytes')
|
27
|
-
raise "Does not advertise support for accept-ranges: bytes!"
|
28
|
-
end
|
29
|
-
|
30
|
-
unless content_length = response.body&.length
|
31
|
-
raise "Could not determine length of response!"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
ensure
|
35
|
-
response&.close
|
36
|
-
end
|
37
|
-
|
38
|
-
Async.logger.info(self) {"Content length: #{content_length/(1024**2)}MiB"}
|
39
|
-
|
40
|
-
parts = []
|
41
|
-
offset = 0
|
42
|
-
chunk_size = 1024*1024
|
43
|
-
|
44
|
-
start_time = Async::Clock.now
|
45
|
-
amount = 0
|
46
|
-
|
47
|
-
while offset < content_length
|
48
|
-
byte_range_start = offset
|
49
|
-
byte_range_end = [offset + chunk_size, content_length].min
|
50
|
-
parts << (byte_range_start...byte_range_end)
|
51
|
-
|
52
|
-
offset += chunk_size
|
53
|
-
end
|
54
|
-
|
55
|
-
Async.logger.info(self) {"Breaking download into #{parts.size} parts..."}
|
56
|
-
|
57
|
-
semaphore = Async::Semaphore.new(8)
|
58
|
-
barrier = Async::Barrier.new(parent: semaphore)
|
59
|
-
|
60
|
-
while !parts.empty?
|
61
|
-
barrier.async do
|
62
|
-
part = parts.shift
|
63
|
-
|
64
|
-
Async.logger.info(self) {"Issuing range request range: bytes=#{part.min}-#{part.max}"}
|
65
|
-
|
66
|
-
response = client.get(endpoint.path, [
|
67
|
-
["range", "bytes=#{part.min}-#{part.max-1}"],
|
68
|
-
*headers
|
69
|
-
])
|
70
|
-
|
71
|
-
if response.success?
|
72
|
-
Async.logger.info(self) {"Got response: #{response}... writing data for #{part}."}
|
73
|
-
written = file.pwrite(response.read, part.min)
|
74
|
-
|
75
|
-
amount += written
|
76
|
-
|
77
|
-
duration = Async::Clock.now - start_time
|
78
|
-
Async.logger.info(self) {"Rate: #{((amount.to_f/(1024**2))/duration).round(2)}MiB/s"}
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
barrier.wait
|
84
|
-
ensure
|
85
|
-
client&.close
|
86
|
-
end
|
data/examples/fetch/Gemfile
DELETED
data/examples/fetch/Gemfile.lock
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
specs:
|
3
|
-
async (1.24.2)
|
4
|
-
console (~> 1.0)
|
5
|
-
nio4r (~> 2.3)
|
6
|
-
timers (~> 4.1)
|
7
|
-
async-container (0.16.2)
|
8
|
-
async (~> 1.0)
|
9
|
-
async-io (~> 1.26)
|
10
|
-
process-group
|
11
|
-
async-http (0.50.5)
|
12
|
-
async (~> 1.23)
|
13
|
-
async-io (~> 1.27.0)
|
14
|
-
async-pool (~> 0.2)
|
15
|
-
protocol-http (~> 0.14.1)
|
16
|
-
protocol-http1 (~> 0.10.0)
|
17
|
-
protocol-http2 (~> 0.11.0)
|
18
|
-
async-http-cache (0.1.2)
|
19
|
-
async-http
|
20
|
-
protocol-http (~> 0.14.4)
|
21
|
-
async-io (1.27.4)
|
22
|
-
async (~> 1.14)
|
23
|
-
async-pool (0.2.0)
|
24
|
-
async (~> 1.8)
|
25
|
-
build-environment (1.13.0)
|
26
|
-
coderay (1.1.2)
|
27
|
-
console (1.8.2)
|
28
|
-
falcon (0.35.6)
|
29
|
-
async (~> 1.13)
|
30
|
-
async-container (~> 0.16.0)
|
31
|
-
async-http (~> 0.50.4)
|
32
|
-
async-http-cache (~> 0.1.0)
|
33
|
-
async-io (~> 1.22)
|
34
|
-
build-environment (~> 1.13)
|
35
|
-
localhost (~> 1.1)
|
36
|
-
process-metrics (~> 0.1.0)
|
37
|
-
rack (>= 1.0)
|
38
|
-
samovar (~> 2.1)
|
39
|
-
ffi (1.12.2)
|
40
|
-
localhost (1.1.6)
|
41
|
-
mapping (1.1.1)
|
42
|
-
method_source (0.9.2)
|
43
|
-
nio4r (2.5.2)
|
44
|
-
process-group (1.2.1)
|
45
|
-
process-terminal (~> 0.2.0)
|
46
|
-
process-metrics (0.1.1)
|
47
|
-
process-terminal (0.2.0)
|
48
|
-
ffi
|
49
|
-
protocol-hpack (1.4.2)
|
50
|
-
protocol-http (0.14.4)
|
51
|
-
protocol-http1 (0.10.2)
|
52
|
-
protocol-http (~> 0.13)
|
53
|
-
protocol-http2 (0.11.1)
|
54
|
-
protocol-hpack (~> 1.4)
|
55
|
-
protocol-http (~> 0.2)
|
56
|
-
pry (0.12.2)
|
57
|
-
coderay (~> 1.1.0)
|
58
|
-
method_source (~> 0.9.0)
|
59
|
-
rack (2.2.2)
|
60
|
-
samovar (2.1.4)
|
61
|
-
console (~> 1.0)
|
62
|
-
mapping (~> 1.0)
|
63
|
-
timers (4.3.0)
|
64
|
-
|
65
|
-
PLATFORMS
|
66
|
-
ruby
|
67
|
-
|
68
|
-
DEPENDENCIES
|
69
|
-
falcon
|
70
|
-
pry
|
71
|
-
rack
|
72
|
-
|
73
|
-
BUNDLED WITH
|
74
|
-
1.17.3
|
data/examples/fetch/README.md
DELETED
data/examples/fetch/config.ru
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'rack'
|
3
|
-
|
4
|
-
class Echo
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
request = Rack::Request.new(env)
|
11
|
-
|
12
|
-
if request.path_info == "/echo"
|
13
|
-
if output = request.body
|
14
|
-
return [200, {}, output.body]
|
15
|
-
else
|
16
|
-
return [200, {}, ["Hello World?"]]
|
17
|
-
end
|
18
|
-
else
|
19
|
-
return @app.call(env)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
use Echo
|
25
|
-
|
26
|
-
use Rack::Static, :urls => [''], :root => 'public', :index => 'index.html'
|
27
|
-
|
28
|
-
run lambda{|env| [404, {}, []]}
|
@@ -1,23 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Streaming Fetch</title>
|
5
|
-
</head>
|
6
|
-
<body>
|
7
|
-
<h1>Streams</h1>
|
8
|
-
|
9
|
-
<button id="stopButton">Start</button>
|
10
|
-
|
11
|
-
<h2>Sent</h2>
|
12
|
-
|
13
|
-
<ul id="sent">
|
14
|
-
</ul>
|
15
|
-
|
16
|
-
<h2>Received</h2>
|
17
|
-
|
18
|
-
<ul id="received">
|
19
|
-
</ul>
|
20
|
-
|
21
|
-
<script src="stream.js"></script>
|
22
|
-
</body>
|
23
|
-
</html>
|
@@ -1,56 +0,0 @@
|
|
1
|
-
const inputStream = new ReadableStream({
|
2
|
-
start(controller) {
|
3
|
-
interval = setInterval(() => {
|
4
|
-
let string = "Hello World!";
|
5
|
-
|
6
|
-
// Add the string to the stream
|
7
|
-
controller.enqueue(string);
|
8
|
-
|
9
|
-
// show it on the screen
|
10
|
-
let listItem = document.createElement('li');
|
11
|
-
listItem.textContent = string;
|
12
|
-
sent.appendChild(listItem);
|
13
|
-
}, 10000);
|
14
|
-
|
15
|
-
stopButton.addEventListener('click', function() {
|
16
|
-
clearInterval(interval);
|
17
|
-
controller.close();
|
18
|
-
})
|
19
|
-
},
|
20
|
-
pull(controller) {
|
21
|
-
// We don't really need a pull in this example
|
22
|
-
},
|
23
|
-
cancel() {
|
24
|
-
// This is called if the reader cancels,
|
25
|
-
// so we should stop generating strings
|
26
|
-
clearInterval(interval);
|
27
|
-
}
|
28
|
-
});
|
29
|
-
|
30
|
-
fetch("/echo", {method: 'POST', body: inputStream})
|
31
|
-
.then(response => {
|
32
|
-
const reader = response.body.getReader();
|
33
|
-
const decoder = new TextDecoder("utf-8");
|
34
|
-
|
35
|
-
function push() {
|
36
|
-
reader.read().then(({done, value}) => {
|
37
|
-
console.log("done:", done, "value:", value);
|
38
|
-
const string = decoder.decode(value);
|
39
|
-
|
40
|
-
// show it on the screen
|
41
|
-
let listItem = document.createElement('li');
|
42
|
-
|
43
|
-
if (done)
|
44
|
-
listItem.textContent = "<EOF>"
|
45
|
-
else
|
46
|
-
listItem.textContent = string;
|
47
|
-
|
48
|
-
received.appendChild(listItem);
|
49
|
-
|
50
|
-
if (done) return;
|
51
|
-
else push();
|
52
|
-
});
|
53
|
-
};
|
54
|
-
|
55
|
-
push();
|
56
|
-
});
|
data/examples/google/search.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "async"
|
4
|
-
require "async/clock"
|
5
|
-
require "protocol/http/middleware"
|
6
|
-
require_relative "../../lib/async/http"
|
7
|
-
|
8
|
-
URL = "https://www.google.com/search"
|
9
|
-
ENDPOINT = Async::HTTP::Endpoint.parse(URL)
|
10
|
-
|
11
|
-
# Console.logger.enable(Async::IO::Stream, Console::Logger::DEBUG)
|
12
|
-
|
13
|
-
class Google < Protocol::HTTP::Middleware
|
14
|
-
def search(term)
|
15
|
-
Async.logger.info(self) {"Searching for #{term}..."}
|
16
|
-
|
17
|
-
self.get("/search?q=#{term}", {"user-agent" => "Hi Google!"})
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
terms = %w{thoughtful fear size payment lethal modern recognise face morning sulky mountainous contain science snow uncle skirt truthful door travel snails closed rotten halting creator teeny-tiny beautiful cherries unruly level follow strip team things suggest pretty warm end cannon bad pig consider airport strengthen youthful fog three walk furry pickle moaning fax book ruddy sigh plate cakes shame stem faulty bushes dislike train sleet one colour behavior bitter suit count loutish squeak learn watery orange idiotic seat wholesale omniscient nostalgic arithmetic instruct committee puffy program cream cake whistle rely encourage war flagrant amusing fluffy prick utter wacky occur daily son check}
|
22
|
-
|
23
|
-
if count = ENV.fetch('COUNT', 20)&.to_i
|
24
|
-
terms = terms.first(count)
|
25
|
-
end
|
26
|
-
|
27
|
-
Async do |task|
|
28
|
-
client = Async::HTTP::Client.new(ENDPOINT)
|
29
|
-
google = Google.new(client)
|
30
|
-
|
31
|
-
google.search("null").finish
|
32
|
-
|
33
|
-
duration = Async::Clock.measure do
|
34
|
-
counts = terms.map do |term|
|
35
|
-
task.async do
|
36
|
-
response = google.search(term)
|
37
|
-
[term, response.read.scan(term).count]
|
38
|
-
end
|
39
|
-
end.map(&:wait).to_h
|
40
|
-
|
41
|
-
Async.logger.info(self, name: 'counts') {counts}
|
42
|
-
end
|
43
|
-
|
44
|
-
Async.logger.info(self, name: 'duration') {duration}
|
45
|
-
ensure
|
46
|
-
google.close
|
47
|
-
end
|