polyphony-http 0.24

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/CHANGELOG.md +6 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +51 -0
  6. data/LICENSE +21 -0
  7. data/README.md +47 -0
  8. data/Rakefile +20 -0
  9. data/TODO.md +59 -0
  10. data/bin/poly +11 -0
  11. data/docs/README.md +38 -0
  12. data/docs/summary.md +60 -0
  13. data/examples/cuba.ru +22 -0
  14. data/examples/happy_eyeballs.rb +37 -0
  15. data/examples/http2_raw.rb +135 -0
  16. data/examples/http_client.rb +28 -0
  17. data/examples/http_get.rb +33 -0
  18. data/examples/http_parse_experiment.rb +123 -0
  19. data/examples/http_proxy.rb +83 -0
  20. data/examples/http_server.js +24 -0
  21. data/examples/http_server.rb +21 -0
  22. data/examples/http_server_forked.rb +29 -0
  23. data/examples/http_server_graceful.rb +27 -0
  24. data/examples/http_server_simple.rb +11 -0
  25. data/examples/http_server_throttled.rb +15 -0
  26. data/examples/http_server_timeout.rb +35 -0
  27. data/examples/http_ws_server.rb +37 -0
  28. data/examples/https_raw_client.rb +12 -0
  29. data/examples/https_server.rb +22 -0
  30. data/examples/https_wss_server.rb +39 -0
  31. data/examples/rack_server.rb +12 -0
  32. data/examples/rack_server_https.rb +19 -0
  33. data/examples/rack_server_https_forked.rb +27 -0
  34. data/examples/websocket_secure_server.rb +27 -0
  35. data/examples/websocket_server.rb +24 -0
  36. data/examples/ws_page.html +34 -0
  37. data/examples/wss_page.html +34 -0
  38. data/lib/polyphony/http.rb +16 -0
  39. data/lib/polyphony/http/client/agent.rb +131 -0
  40. data/lib/polyphony/http/client/http1.rb +129 -0
  41. data/lib/polyphony/http/client/http2.rb +180 -0
  42. data/lib/polyphony/http/client/response.rb +32 -0
  43. data/lib/polyphony/http/client/site_connection_manager.rb +109 -0
  44. data/lib/polyphony/http/server.rb +49 -0
  45. data/lib/polyphony/http/server/http1.rb +267 -0
  46. data/lib/polyphony/http/server/http2.rb +78 -0
  47. data/lib/polyphony/http/server/http2_stream.rb +135 -0
  48. data/lib/polyphony/http/server/rack.rb +64 -0
  49. data/lib/polyphony/http/server/request.rb +118 -0
  50. data/lib/polyphony/http/version.rb +7 -0
  51. data/lib/polyphony/websocket.rb +59 -0
  52. data/polyphony-http.gemspec +34 -0
  53. data/test/coverage.rb +45 -0
  54. data/test/eg.rb +27 -0
  55. data/test/helper.rb +35 -0
  56. data/test/run.rb +5 -0
  57. data/test/test_http_server.rb +313 -0
  58. metadata +245 -0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ ::Exception.__disable_sanitized_backtrace__ = true
7
+
8
+ opts = {
9
+ reuse_addr: true,
10
+ dont_linger: true
11
+ }
12
+
13
+ server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
14
+
15
+ puts 'Listening on port 1234'
16
+
17
+ child_pids = []
18
+ 4.times do
19
+ pid = Polyphony.fork do
20
+ puts "forked pid: #{Process.pid}"
21
+ server.each do |req|
22
+ req.respond("Hello world! from pid: #{Process.pid}\n")
23
+ end
24
+ rescue Interrupt
25
+ end
26
+ child_pids << pid
27
+ end
28
+
29
+ child_pids.each { |pid| Gyro::Child.new(pid).await }
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'polyphony/http'
6
+
7
+ opts = {
8
+ reuse_addr: true,
9
+ dont_linger: true
10
+ }
11
+
12
+ server = spin do
13
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
14
+ req.respond("Hello world!\n")
15
+ end
16
+ end
17
+
18
+ trap('SIGHUP') do
19
+ puts 'got hup'
20
+ server.interrupt
21
+ end
22
+
23
+ puts "pid: #{Process.pid}"
24
+ puts 'Send HUP to stop gracefully'
25
+ puts 'Listening on port 1234...'
26
+
27
+ suspend
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ puts "pid: #{Process.pid}"
7
+ puts 'Listening on port 1234...'
8
+
9
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234) do |req|
10
+ req.respond("Hello world!\n")
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ $throttler = throttle(1000)
7
+ opts = { reuse_addr: true, dont_linger: true }
8
+ spin do
9
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
10
+ $throttler.call { req.respond("Hello world!\n") }
11
+ end
12
+ end
13
+
14
+ puts "pid: #{Process.pid}"
15
+ puts 'Listening on port 1234...'
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ opts = {
7
+ reuse_addr: true,
8
+ dont_linger: true
9
+ }
10
+
11
+ def timeout_handler(timeout, &handler)
12
+ ->(req) do
13
+ cancel_after(timeout) { handler.(req) }
14
+ rescue Polyphony::Cancel
15
+ req.respond("timeout\n", ':status' => 502)
16
+ end
17
+ end
18
+
19
+ sleep 0
20
+
21
+ spin do
22
+ Polyphony::HTTP::Server.serve(
23
+ '0.0.0.0',
24
+ 1234,
25
+ opts,
26
+ &timeout_handler(0.1) do |req|
27
+ sleep rand(0.01..0.2)
28
+ req.respond("Hello timeout world!\n")
29
+ end
30
+ )
31
+ end
32
+
33
+ puts "pid: #{Process.pid}"
34
+ puts 'Listening on port 1234...'
35
+ suspend
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+ require 'polyphony/websocket'
6
+
7
+ def ws_handler(conn)
8
+ timer = spin do
9
+ throttled_loop(1) do
10
+ conn << Time.now.to_s
11
+ end
12
+ end
13
+ while (msg = conn.recv)
14
+ conn << "you said: #{msg}"
15
+ end
16
+ ensure
17
+ timer.stop
18
+ end
19
+
20
+ opts = {
21
+ reuse_addr: true,
22
+ dont_linger: true,
23
+ upgrade: {
24
+ websocket: Polyphony::Websocket.handler(&method(:ws_handler))
25
+ }
26
+ }
27
+
28
+ HTML = IO.read(File.join(__dir__, 'ws_page.html'))
29
+
30
+ spin do
31
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
32
+ req.respond(HTML, 'Content-Type' => 'text/html')
33
+ end
34
+ end
35
+
36
+ puts "pid: #{Process.pid}"
37
+ puts 'Listening on port 1234...'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ t0 = Time.now
7
+ io = Polyphony::Net.tcp_connect('google.com', 443, secure: true)
8
+ io.write("GET / HTTP/1.0\r\nHost: realiteq.net\r\n\r\n")
9
+ reply = io.read
10
+ puts "time: #{Time.now - t0}"
11
+ puts
12
+ puts reply
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+ require 'localhost/authority'
6
+
7
+ authority = Localhost::Authority.fetch
8
+ opts = {
9
+ reuse_addr: true,
10
+ dont_linger: true,
11
+ secure_context: authority.server_context
12
+ }
13
+
14
+ puts "pid: #{Process.pid}"
15
+ puts 'Listening on port 1234...'
16
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
17
+ req.respond("Hello world!\n")
18
+ # req.send_headers
19
+ # req.send_chunk("Method: #{req.method}\n")
20
+ # req.send_chunk("Path: #{req.path}\n")
21
+ # req.send_chunk("Query: #{req.query.inspect}\n", done: true)
22
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+ require 'localhost/authority'
6
+
7
+ def ws_handler(conn)
8
+ timer = spin do
9
+ throttled_loop(1) do
10
+ conn << Time.now.to_s
11
+ rescue StandardError
12
+ nil
13
+ end
14
+ end
15
+ while (msg = conn.recv)
16
+ puts "msg: #{msg}"
17
+ # conn << "you said: #{msg}"
18
+ end
19
+ ensure
20
+ timer.stop
21
+ end
22
+
23
+ authority = Localhost::Authority.fetch
24
+ opts = {
25
+ reuse_addr: true,
26
+ dont_linger: true,
27
+ secure_context: authority.server_context,
28
+ upgrade: {
29
+ websocket: Polyphony::Websocket.handler(&method(:ws_handler))
30
+ }
31
+ }
32
+
33
+ HTML = IO.read(File.join(__dir__, 'wss_page.html'))
34
+
35
+ puts "pid: #{Process.pid}"
36
+ puts 'Listening on port 1234...'
37
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
38
+ req.respond(HTML, 'Content-Type' => 'text/html')
39
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
7
+ app = Polyphony::HTTP::Rack.load(app_path)
8
+ opts = { reuse_addr: true, dont_linger: true }
9
+
10
+ puts 'listening on port 1234'
11
+ puts "pid: #{Process.pid}"
12
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts, &app)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+ require 'localhost/authority'
6
+
7
+ app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
8
+ app = Polyphony::HTTP::Rack.load(app_path)
9
+
10
+ authority = Localhost::Authority.fetch
11
+ opts = {
12
+ reuse_addr: true,
13
+ dont_linger: true,
14
+ secure_context: authority.server_context
15
+ }
16
+
17
+ puts 'listening on port 1234'
18
+ puts "pid: #{Process.pid}"
19
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts, &app)
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+ require 'localhost/authority'
6
+
7
+ app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
8
+ app = Polyphony::HTTP::Rack.load(app_path)
9
+
10
+ authority = Localhost::Authority.fetch
11
+ opts = {
12
+ reuse_addr: true,
13
+ dont_linger: true,
14
+ secure_context: authority.server_context
15
+ }
16
+ server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
17
+ puts 'Listening on port 1234'
18
+
19
+ child_pids = []
20
+ 4.times do
21
+ child_pids << Polyphony.fork do
22
+ puts "forked pid: #{Process.pid}"
23
+ server.each(&app)
24
+ end
25
+ end
26
+
27
+ child_pids.each { |pid| EV::Child.new(pid).await }
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+ require 'localhost/authority'
6
+
7
+ def ws_handler(conn)
8
+ while (msg = conn.recv)
9
+ conn << "you said: #{msg}"
10
+ end
11
+ end
12
+
13
+ authority = Localhost::Authority.fetch
14
+ opts = {
15
+ reuse_addr: true,
16
+ dont_linger: true,
17
+ upgrade: {
18
+ websocket: Polyphony::Websocket.handler(&method(:ws_handler))
19
+ },
20
+ secure_context: authority.server_context
21
+ }
22
+
23
+ puts "pid: #{Process.pid}"
24
+ puts 'Listening on port 1234...'
25
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
26
+ req.respond("Hello world!\n")
27
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/http'
5
+
6
+ def ws_handler(conn)
7
+ while (msg = conn.recv)
8
+ conn << "you said: #{msg}"
9
+ end
10
+ end
11
+
12
+ opts = {
13
+ reuse_addr: true,
14
+ dont_linger: true,
15
+ upgrade: {
16
+ websocket: Polyphony::Websocket.handler(&method(:ws_handler))
17
+ }
18
+ }
19
+
20
+ puts "pid: #{Process.pid}"
21
+ puts 'Listening on port 1234...'
22
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts) do |req|
23
+ req.respond("Hello world!\n")
24
+ end
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Websocket Client</title>
5
+ </head>
6
+ <body>
7
+ <script>
8
+ var connect = function () {
9
+ var exampleSocket = new WebSocket("ws://localhost:1234");
10
+
11
+ exampleSocket.onopen = function (event) {
12
+ document.querySelector('#status').innerText = 'connected';
13
+ exampleSocket.send("Can you hear me?");
14
+ };
15
+ exampleSocket.onclose = function (event) {
16
+ console.log('onclose');
17
+ document.querySelector('#status').innerText = 'disconnected';
18
+ setTimeout(function () {
19
+ // exampleSocket.removeAllListeners();
20
+ connect();
21
+ }, 1000);
22
+ }
23
+ exampleSocket.onmessage = function (event) {
24
+ document.querySelector('#msg').innerText = event.data;
25
+ console.log(event.data);
26
+ }
27
+ };
28
+
29
+ connect();
30
+ </script>
31
+ <h1 id="status">disconnected</h1>
32
+ <h1 id="msg"></h1>
33
+ </body>
34
+ </html>
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Websocket Client</title>
5
+ </head>
6
+ <body>
7
+ <script>
8
+ var connect = function () {
9
+ var exampleSocket = new WebSocket("/");
10
+
11
+ exampleSocket.onopen = function (event) {
12
+ document.querySelector('#status').innerText = 'connected';
13
+ exampleSocket.send("Can you hear me?");
14
+ };
15
+ exampleSocket.onclose = function (event) {
16
+ console.log('onclose');
17
+ document.querySelector('#status').innerText = 'disconnected';
18
+ setTimeout(function () {
19
+ // exampleSocket.removeAllListeners();
20
+ connect();
21
+ }, 1000);
22
+ }
23
+ exampleSocket.onmessage = function (event) {
24
+ document.querySelector('#msg').innerText = event.data;
25
+ console.log(event.data);
26
+ }
27
+ };
28
+
29
+ connect();
30
+ </script>
31
+ <h1 id="status">disconnected</h1>
32
+ <h1 id="msg"></h1>
33
+ </body>
34
+ </html>
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polyphony'
4
+
5
+ module Polyphony
6
+ # HTTP imports (loaded dynamically)
7
+ module HTTP
8
+ auto_import(
9
+ Agent: './http/client/agent',
10
+ Rack: './http/server/rack',
11
+ Server: './http/server'
12
+ )
13
+ end
14
+ end
15
+
16
+ export_default Polyphony::HTTP