polyphony-http 0.24

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7ca781bfd155cadb774a4b9578c947aed555ad14af76d282d17be7a8f765faae
4
+ data.tar.gz: 4317fb42a26ca09d769d78905a89e6c12c0eb0bbc4b18b7b74ff5132a6ac90ac
5
+ SHA512:
6
+ metadata.gz: 82d55c481540b596463e18fb9eea87f92e52c53e5fab04cbddeca54b4a5462002f4b58657dc0c5afc3cec763999798e564fed3a82fe234dd98fe565318602f57
7
+ data.tar.gz: fada33a093f8c9967a0927173c57d03d0e82c27b80de6a113e435810f2bd8d05f894dc467c66883f4196ed72702bdef0e207892ac1b233501fc96cfd8ca749d6
data/.gitignore ADDED
@@ -0,0 +1,56 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ # Ignore Byebug command history file.
17
+ .byebug_history
18
+
19
+ ## Specific to RubyMotion:
20
+ .dat*
21
+ .repl_history
22
+ build/
23
+ *.bridgesupport
24
+ build-iPhoneOS/
25
+ build-iPhoneSimulator/
26
+
27
+ ## Specific to RubyMotion (use of CocoaPods):
28
+ #
29
+ # We recommend against adding the Pods directory to your .gitignore. However
30
+ # you should judge for yourself, the pros and cons are mentioned at:
31
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32
+ #
33
+ # vendor/Pods/
34
+
35
+ ## Documentation cache and generated files:
36
+ /.yardoc/
37
+ /_yardoc/
38
+ /doc/
39
+ /rdoc/
40
+
41
+ ## Environment normalization:
42
+ /.bundle/
43
+ /vendor/bundle
44
+ /lib/bundler/man/
45
+
46
+ # for a library or gem, you might want to ignore these files since the code is
47
+ # intended to run in multiple environments; otherwise, check them in:
48
+ # Gemfile.lock
49
+ # .ruby-version
50
+ # .ruby-gemset
51
+
52
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53
+ .rvmrc
54
+
55
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
+ # .rubocop-https?--*
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ 0.24 2020-01-08
2
+ ---------------
3
+
4
+ * Move HTTP to separate polyphony-http gem
5
+
6
+ For earlier changes look at the Polyphony changelog.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ polyphony-http (0.24)
5
+ http-2 (~> 0.10.0)
6
+ http_parser.rb (~> 0.6.0)
7
+ modulation (~> 1.0)
8
+ polyphony (~> 0.30)
9
+ rack (~> 2.0.8)
10
+ websocket (~> 1.2.8)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ ansi (1.5.0)
16
+ builder (3.2.4)
17
+ docile (1.3.2)
18
+ http-2 (0.10.1)
19
+ http_parser.rb (0.6.0)
20
+ json (2.1.0)
21
+ localhost (1.1.4)
22
+ minitest (5.11.3)
23
+ minitest-reporters (1.4.2)
24
+ ansi
25
+ builder
26
+ minitest (>= 5.0)
27
+ ruby-progressbar
28
+ modulation (1.0)
29
+ polyphony (0.30)
30
+ modulation (~> 1.0)
31
+ rack (2.0.9)
32
+ ruby-progressbar (1.10.1)
33
+ simplecov (0.17.1)
34
+ docile (~> 1.1)
35
+ json (>= 1.8, < 3)
36
+ simplecov-html (~> 0.10.0)
37
+ simplecov-html (0.10.2)
38
+ websocket (1.2.8)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ localhost (~> 1.1.4)
45
+ minitest (~> 5.11.3)
46
+ minitest-reporters (~> 1.4.2)
47
+ polyphony-http!
48
+ simplecov (~> 0.17.1)
49
+
50
+ BUNDLED WITH
51
+ 2.1.3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Sharon Rosner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Polyphony - Easy Concurrency for Ruby
2
+
3
+ [DOCS](https://dfab.gitbook.io/polyphony) |
4
+ [EXAMPLES](examples)
5
+
6
+ > Polyphony \| pəˈlɪf\(ə\)ni \| _Music_ - the style of simultaneously combining a number of parts, each forming an individual melody and harmonizing with each other.
7
+
8
+ ## What is Polyphony
9
+
10
+ Polyphony is a library for building concurrent applications in Ruby. Polyphony harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html) to provide a cooperative, sequential coprocess-based concurrency model. Under the hood, Polyphony uses [libev](https://github.com/enki/libev) as a high-performance event reactor that provides timers, I/O watchers and other asynchronous event primitives.
11
+
12
+ Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and `Socket` in a concurrent fashion without having to resort to threads. Polyphony takes care of context-switching automatically whenever a blocking call like `Socket#accept` or `IO#read` is issued.
13
+
14
+ ## Features
15
+
16
+ * **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server
17
+ with TLS/SSL termination, automatic ALPN protocol selection, and body
18
+ streaming**.
19
+ * Co-operative scheduling of concurrent tasks using Ruby fibers.
20
+ * High-performance event reactor for handling I/O events and timers.
21
+ * Natural, sequential programming style that makes it easy to reason about
22
+ concurrent code.
23
+ * Abstractions and constructs for controlling the execution of concurrent code:
24
+ coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
25
+ * Code can use native networking classes and libraries, growing support for
26
+ third-party gems such as `pg` and `redis`.
27
+ * Use stdlib classes such as `TCPServer`, `TCPSocket` and
28
+ * HTTP 1 / HTTP 2 client agent with persistent connections.
29
+ * Competitive performance and scalability characteristics, in terms of both
30
+ throughput and memory consumption.
31
+
32
+ ## Prior Art
33
+
34
+ Polyphony draws inspiration from the following, in no particular order:
35
+
36
+ * [nio4r](https://github.com/socketry/nio4r/) and [async](https://github.com/socketry/async)
37
+ (Polyphony's C-extension code is largely a spinoff of
38
+ [nio4r's](https://github.com/socketry/nio4r/tree/master/ext))
39
+ * [EventMachine](https://github.com/eventmachine/eventmachine)
40
+ * [Trio](https://trio.readthedocs.io/)
41
+ * [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually,
42
+ Erlang in general)
43
+
44
+ ## Documentation
45
+
46
+ The complete documentation for Polyphony could be found on the
47
+ [Polyphony website](https://dfab.gitbook.io/polyphony).
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/clean"
5
+
6
+ # frozen_string_literal: true
7
+
8
+ require "rake/extensiontask"
9
+ Rake::ExtensionTask.new("gyro_ext") do |ext|
10
+ ext.ext_dir = "ext/gyro"
11
+ end
12
+
13
+ task :default => [:compile, :test]
14
+ task :test do
15
+ exec 'ruby test/run.rb'
16
+ end
17
+
18
+ task default: %w[compile]
19
+
20
+ CLEAN.include "**/*.o", "**/*.so", "**/*.bundle", "**/*.jar", "pkg", "tmp"
data/TODO.md ADDED
@@ -0,0 +1,59 @@
1
+ # HTTP Client Agent
2
+
3
+ The concurrency model and the fact that we want to serve the response object on
4
+ receiving headers and let the user lazily read the response body, means we'll
5
+ need to change the API to accept a block:
6
+
7
+ ```ruby
8
+ # current API
9
+ resp = Agent.get('http://acme.org')
10
+ puts resp.body
11
+
12
+ # proposed API
13
+ Agent.get('http://acme.org') do |resp|
14
+ puts resp.body
15
+ end
16
+ ```
17
+
18
+ While the block is running, the connection adapter is acquired. Once the block
19
+ is done running, the request (and response) can be discarded. The problem with
20
+ that if we spin up a coprocess from that block we risk all kinds of race
21
+ conditions and weird behaviours.
22
+
23
+ A compromise might be to allow the two: doing a `get` without providing a block
24
+ will return a response object that already has the body (i.e. the entire
25
+ response has already been received). Doing a `get` with a block will invoke the
26
+ block once headers are received, letting the user's code stream the body:
27
+
28
+ ```ruby
29
+ def request(ctx, &block)
30
+ ...
31
+ connection_manager.acquire do |adapter|
32
+ response = adapter.request(ctx)
33
+ if block
34
+ block.(response)
35
+ else
36
+ # wait for body
37
+ response.body
38
+ end
39
+ response
40
+ end
41
+ end
42
+ ```
43
+
44
+ # Roadmap:
45
+
46
+ ## 0.25 Full Rack adapter implementation
47
+
48
+ - Work better mechanism supervising multiple coprocesses (`when_done` feels a
49
+ bit hacky)
50
+ - Add supervisor test
51
+ - Homogenize HTTP 1 and HTTP 2 headers - upcase ? downcase ?
52
+ - find some demo Rack apps and test with Polyphony
53
+
54
+ ## 0.26 Working Sinatra application
55
+
56
+ - app with database access (postgresql)
57
+ - benchmarks!
58
+
59
+ ## 0.27 Testing & docs
data/bin/poly ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative('../lib/polyphony/http')
4
+
5
+ app_path = ARGV.first || './config.ru'
6
+ app = Polyphony::HTTP::Rack.load(app_path)
7
+ opts = { reuse_addr: true, dont_linger: true }
8
+
9
+ puts "listening on port 1234"
10
+ puts "pid: #{Process.pid}"
11
+ Polyphony::HTTP::Server.serve('0.0.0.0', 1234, opts, &app)
data/docs/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # Polyphony - Easy Concurrency for Ruby
2
+
3
+ > Polyphony \| pəˈlɪf\(ə\)ni \|
4
+ > 1. _Music_ the style of simultaneously combining a number of parts, each forming an individual melody and harmonizing with each other.
5
+ > 2. _Programming_ a Ruby gem for concurrent programming focusing on performance and developer happiness.
6
+
7
+ Polyphony is a library for building concurrent applications in Ruby. Polyphony harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html) to provide a cooperative, sequential coprocess-based concurrency model. Under the hood, Polyphony uses [libev](https://github.com/enki/libev) as a high-performance event reactor that provides timers, I/O watchers and other asynchronous event primitives.
8
+
9
+ Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and `Socket` in a concurrent fashion without having to resort to threads. Polyphony takes care of context-switching automatically whenever a blocking call like `Socket#accept` or `IO#read` is issued.
10
+
11
+ ## Features
12
+
13
+ * **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server with TLS/SSL termination, automatic ALPN protocol selection, and body streaming**.
14
+ * Co-operative scheduling of concurrent tasks using Ruby fibers.
15
+ * High-performance event reactor for handling I/O events and timers.
16
+ * Natural, sequential programming style that makes it easy to reason about concurrent code.
17
+ * Abstractions and constructs for controlling the execution of concurrent code: coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
18
+ * Code can use native networking classes and libraries, growing support for third-party gems such as `pg` and `redis`.
19
+ * Use stdlib classes such as `TCPServer` and `TCPSocket` and `Net::HTTP`.
20
+ * HTTP 1 / HTTP 2 client agent with persistent connections.
21
+ * Competitive performance and scalability characteristics, in terms of both throughput and memory consumption.
22
+
23
+ ## Prior Art
24
+
25
+ Polyphony draws inspiration from the following, in no particular order:
26
+
27
+ * [nio4r](https://github.com/socketry/nio4r/) and [async](https://github.com/socketry/async) (Polyphony's C-extension code is largely a spinoff of [nio4r's](https://github.com/socketry/nio4r/tree/master/ext))
28
+ * [EventMachine](https://github.com/eventmachine/eventmachine)
29
+ * [Trio](https://trio.readthedocs.io/)
30
+ * [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually, Erlang in general)
31
+
32
+ ## Going further
33
+
34
+ To learn more about using Polyphony to build concurrent applications, read the technical overview below, or look at the [included examples](https://github.com/digital-fabric/polyphony/tree/9e0f3b09213156bdf376ef33684ef267517f06e8/examples/README.md). A thorough reference is forthcoming.
35
+
36
+ ## Contributing to Polyphony
37
+
38
+ Issues and pull requests will be gladly accepted. Please use the git repository at https://github.com/digital-fabric/polyphony as your primary point of departure for contributing.
data/docs/summary.md ADDED
@@ -0,0 +1,60 @@
1
+ # Table of contents
2
+
3
+ * [Polyphony - Easy Concurrency for Ruby](../README.md)
4
+
5
+ ## Getting Started
6
+
7
+ * [Installing](getting-started/installing.md)
8
+ * [Tutorial](getting-started/tutorial.md)
9
+
10
+ ## Technical overview
11
+
12
+ * [Design Principles](technical-overview/design-principles.md)
13
+ * [Concurrency the Easy Way](technical-overview/concurrency.md)
14
+ * [How Fibers are Scheduled](technical-overview/fiber-scheduling.md)
15
+ * [Exception Handling](technical-overview/exception-handling.md)
16
+ * [Frequently Asked Questions](technical-overview/faq.md)
17
+
18
+ ## How To
19
+
20
+ * [Make an echo server](howto/echo-server.md)
21
+ * [Make an HTTP server](howto/http-server.md)
22
+ * [Make a Websocket server](howto/websocket-server.md)
23
+ * [Use timers](howto/timers.md)
24
+ * [Throttle recurrent operations](howto/throttle.md)
25
+ * [Cancel ongoing operations](howto/cancel.md)
26
+ * [Control coprocesses](howto/coprocesses.md)
27
+ * [Synchronize concurrent operations](howto/synchronize.md)
28
+ * [Perform CPU-bound operations](howto/cpu-bound.md)
29
+ * [Control backpressure](howto/backpressure.md)
30
+ * [Fork worker processes](howto/worker-processes.md)
31
+
32
+ ## Polyphony extensions
33
+
34
+ * [Postgresql](extensions/pg)
35
+ * [Redis](extensions/redis)
36
+ * [IRB](extensions/irb)
37
+ * [Throttlers](#)
38
+ * [Resource Pools](#)
39
+ * [Synchronisation](#)
40
+ * [Web Server](user-guide/web-server.md)
41
+ * [Websocket Server](#)
42
+ * [Reactor API](#)
43
+
44
+ ## API Reference
45
+
46
+ * [Polyphony::CancelScope](#)
47
+ * [Polyphony::Coprocess](#)
48
+ * [Gyro](#)
49
+ * [Gyro::Async](#)
50
+ * [Gyro::Child](#)
51
+ * [Gyro::IO](#)
52
+ * [Gyro::Timer](#)
53
+ * [Kernel](#)
54
+ * [Polyphony](#)
55
+ * [Polyphony::Mutex](#)
56
+ * [Polyphony::Pulser](#)
57
+ * [Polyphony::ResourcePool](#)
58
+ * [Polyphony::Throttler](#)
59
+
60
+ ## [Contributing to Polyphony](contributing.md)
data/examples/cuba.ru ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuba'
4
+ require 'cuba/safe'
5
+
6
+ Cuba.use Rack::Session::Cookie, secret: '__a_very_long_string__'
7
+
8
+ Cuba.plugin Cuba::Safe
9
+
10
+ Cuba.define do
11
+ on get do
12
+ on 'hello' do
13
+ res.write 'Hello world!'
14
+ end
15
+
16
+ on root do
17
+ res.redirect '/hello'
18
+ end
19
+ end
20
+ end
21
+
22
+ run Cuba
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # idea taken from the example given in trio:
4
+ # https://www.youtube.com/watch?v=oLkfnc_UMcE
5
+
6
+ require 'bundler/setup'
7
+ require 'polyphony/http'
8
+
9
+ def try_connect(target, supervisor)
10
+ puts "trying #{target[2]}"
11
+ socket = Polyphony::Net.tcp_connect(target[2], 80)
12
+ # connection successful
13
+ supervisor.stop!([target[2], socket])
14
+ rescue IOError, SystemCallError
15
+ # ignore error
16
+ end
17
+
18
+ def happy_eyeballs(hostname, port, max_wait_time: 0.025)
19
+ targets = Socket.getaddrinfo(hostname, port, :INET, :STREAM)
20
+ t0 = Time.now
21
+ cancel_after(5) do
22
+ success = supervise do |supervisor|
23
+ targets.each_with_index do |t, idx|
24
+ sleep(max_wait_time) if idx > 0
25
+ supervisor.spin { try_connect(t, supervisor) }
26
+ end
27
+ end
28
+ if success
29
+ puts format('success: %s (%.3fs)', success[0], Time.now - t0)
30
+ else
31
+ puts "timed out (#{Time.now - t0}s)"
32
+ end
33
+ end
34
+ end
35
+
36
+ # Let's try it out:
37
+ happy_eyeballs('debian.org', 'https')