tipi 0.36 → 0.39

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: 82a6e09e9b1777016ef5da412c7e60d7224c8ea4d2839b486f02ccda2774d522
4
- data.tar.gz: 0d2ae87d9c2525432417699553670503528e52a6ca3c0d400e8a7a0bfeae0142
3
+ metadata.gz: 02c5ce4798e1961ca59798b75bafe158570e777661f6fc8667169553df3cd3f4
4
+ data.tar.gz: 405bfa3526efaad394d34840764dc131230a366574cfc52b63344a6d5cdfe6dd
5
5
  SHA512:
6
- metadata.gz: db5d80d15a5fc164b09461fe91bb0a35549910f6e7783583f0df838c3d5d5379b797cb67bd7d19236b9a5388a2ba1957fa081092ca103643cd028d475668cfcd
7
- data.tar.gz: f9d965dcc195568900d3a772892925bea90fc45b8e8a5e8ad4c0370323a388b26c66e6a85fc629c73d9b028caebc4f6bf27c430991ba9e1e2dd4335fce98bc6a
6
+ metadata.gz: 754bd35136e4e004e6bd782eb2060d933b2439c437a9c2a966529e0465c42096b97fa08b1dae04b7a3c8aa0c77c367fd5395d215cca73fd7cf1aa80c297b3178
7
+ data.tar.gz: 24425d798c076ad43b4fa45b641a90a5e801dfe35acd6217e8a8fc97ea546f18457fc9a8653e47f7bd5f696da885ef86db8db36385dcaa79ccbcf9d42d64081c
@@ -22,6 +22,6 @@ jobs:
22
22
  - name: Install dependencies
23
23
  run: |
24
24
  gem install bundler
25
- bundle install
25
+ POLYPHONY_USE_LIBEV=1 bundle install
26
26
  - name: Run tests
27
27
  run: bundle exec rake test
data/.gitignore CHANGED
@@ -54,3 +54,4 @@ build-iPhoneSimulator/
54
54
 
55
55
  # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
56
  # .rubocop-https?--*
57
+ log
data/CHANGELOG.md CHANGED
@@ -1,68 +1,89 @@
1
+ ## 0.39 2021-06-20
2
+
3
+ - More work on DF server
4
+ - Fix HTTP2StreamHandler#send_headers
5
+ - Various fixes to HTTP/2 adapter
6
+ - Fix host detection for HTTP/2 connections
7
+ - Fix HTTP/1 adapter #respond with nil body
8
+ - Fix HTTP1Adapter#send_headers
9
+
10
+ ## 0.38 2021-03-09
11
+
12
+ - Don't use chunked transfer encoding for non-streaming responses
13
+
14
+ ## 0.37.2 2021-03-08
15
+
16
+ - Fix header formatting when header value is an array
17
+
18
+ ## 0.37 2021-02-15
19
+
20
+ - Update upgrade mechanism to work with updated Qeweney API
21
+
1
22
  ## 0.36 2021-02-12
2
23
 
3
- * Use `Qeweney::Status` constants
24
+ - Use `Qeweney::Status` constants
4
25
 
5
26
  ## 0.35 2021-02-10
6
27
 
7
- * Extract Request class into separate [qeweney](https://github.com/digital-fabric/qeweney) gem
28
+ - Extract Request class into separate [qeweney](https://github.com/digital-fabric/qeweney) gem
8
29
 
9
30
  ## 0.34 2021-02-07
10
31
 
11
- * Implement digital fabric service and agents
12
- * Add multipart and urlencoded form data parsing
13
- * Improve request body reading behaviour
14
- * Add more `Request` information methods
15
- * Add access to connection for HTTP2 requests
16
- * Allow calling `Request#send_chunk` with empty chunk
17
- * Add support for handling protocol upgrades from within request handler
32
+ - Implement digital fabric service and agents
33
+ - Add multipart and urlencoded form data parsing
34
+ - Improve request body reading behaviour
35
+ - Add more `Request` information methods
36
+ - Add access to connection for HTTP2 requests
37
+ - Allow calling `Request#send_chunk` with empty chunk
38
+ - Add support for handling protocol upgrades from within request handler
18
39
 
19
40
  ## 0.33 2020-11-20
20
41
 
21
- * Update code for Polyphony 0.47.5
22
- * Add support for Rack::File body to Tipi::RackAdapter
42
+ - Update code for Polyphony 0.47.5
43
+ - Add support for Rack::File body to Tipi::RackAdapter
23
44
 
24
45
  ## 0.32 2020-08-14
25
46
 
26
- * Respond with array of strings instead of concatenating for HTTP 1
27
- * Use read_loop instead of readpartial
28
- * Fix http upgrade test
47
+ - Respond with array of strings instead of concatenating for HTTP 1
48
+ - Use read_loop instead of readpartial
49
+ - Fix http upgrade test
29
50
 
30
51
  ## 0.31 2020-07-28
31
52
 
32
- * Fix websocket server code
33
- * Implement configuration layer (WIP)
34
- * Improve performance of rack adapter
53
+ - Fix websocket server code
54
+ - Implement configuration layer (WIP)
55
+ - Improve performance of rack adapter
35
56
 
36
57
  ## 0.30 2020-07-15
37
58
 
38
- * Rename project to Tipi
39
- * Rearrange source code
40
- * Remove HTTP client code (to be developed eventually into a separate gem)
41
- * Fix header rendering in rack adapter (#2)
59
+ - Rename project to Tipi
60
+ - Rearrange source code
61
+ - Remove HTTP client code (to be developed eventually into a separate gem)
62
+ - Fix header rendering in rack adapter (#2)
42
63
 
43
64
  ## 0.29 2020-07-06
44
65
 
45
- * Use IO#read_loop
66
+ - Use IO#read_loop
46
67
 
47
68
  ## 0.28 2020-07-03
48
69
 
49
- * Update with API changes from Polyphony >= 0.41
70
+ - Update with API changes from Polyphony >= 0.41
50
71
 
51
72
  ## 0.27 2020-04-14
52
73
 
53
- * Remove modulation dependency
74
+ - Remove modulation dependency
54
75
 
55
76
  ## 0.26 2020-03-03
56
77
 
57
- * Fix `Server#listen`
78
+ - Fix `Server#listen`
58
79
 
59
80
  ## 0.25 2020-02-19
60
81
 
61
- * Ensure server socket is closed upon stopping loop
62
- * Fix `Request#format_header_lines`
82
+ - Ensure server socket is closed upon stopping loop
83
+ - Fix `Request#format_header_lines`
63
84
 
64
85
  ## 0.24 2020-01-08
65
86
 
66
- * Move HTTP to separate polyphony-http gem
87
+ - Move HTTP to separate polyphony-http gem
67
88
 
68
89
  For earlier changes look at the Polyphony changelog.
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tipi (0.36)
4
+ tipi (0.39)
5
5
  http-2 (~> 0.10.0)
6
6
  http_parser.rb (~> 0.6.0)
7
7
  msgpack (~> 1.4.2)
8
- polyphony (~> 0.51.0)
9
- qeweney (~> 0.3)
8
+ polyphony (~> 0.55.0)
9
+ qeweney (~> 0.9.1)
10
10
  rack (>= 2.0.8, < 2.3.0)
11
11
  websocket (~> 1.2.8)
12
12
 
@@ -28,8 +28,8 @@ GEM
28
28
  minitest (>= 5.0)
29
29
  ruby-progressbar
30
30
  msgpack (1.4.2)
31
- polyphony (0.51.0)
32
- qeweney (0.3)
31
+ polyphony (0.55.0)
32
+ qeweney (0.9.1)
33
33
  escape_utils (~> 1.2.1)
34
34
  rack (2.2.3)
35
35
  rake (12.3.3)
data/TODO.md CHANGED
@@ -1,8 +1,82 @@
1
- # Digital Fabric
1
+ ## Add an API for reading a request body chunk into an IO (pipe)
2
+
3
+ ```ruby
4
+ # currently
5
+ chunk = req.next_chunk
6
+ # or
7
+ req.each_chunk { |c| do_something(c) }
8
+
9
+ # what we'd like to do
10
+ r, w = IO.pipe
11
+ len = req.splice_chunk(w)
12
+ sock << "Here comes a chunk of #{len} bytes\n"
13
+ sock.splice(r, len)
14
+
15
+ # or:
16
+ r, w = IO.pipe
17
+ req.splice_each_chunk(w) do |len|
18
+ sock << "Here comes a chunk of #{len} bytes\n"
19
+ sock.splice(r, len)
20
+ end
21
+ ```
22
+
23
+ # HTTP/1.1 parser
24
+
25
+ - httparser.rb is not actively updated
26
+ - the httparser.rb C parser code comes originally from https://github.com/nodejs/llhttp
27
+ - there's a Ruby gem https://github.com/metabahn/llhttp, but its API is too low-level
28
+ (lots of callbacks, headers need to be retained across callbacks)
29
+ - the basic idea is to import the C-code, then build a parser object with the following
30
+ callbacks:
31
+
32
+ ```ruby
33
+ on_headers_complete(headers)
34
+ on_body_chunk(chunk)
35
+ on_message_complete
36
+ ```
37
+
38
+ - The llhttp gem's C-code is here: https://github.com/metabahn/llhttp/tree/main/mri
39
+
40
+ - Actually, if you do a C extension, instead of a callback-based API, we can
41
+ design a blocking API:
42
+
43
+ ```ruby
44
+ parser = Tipi::HTTP1::Parser.new
45
+ parser.each_request(socket) do |headers|
46
+ request = Request.new(normalize_headers(headers))
47
+ handle_request(request)
48
+ end
49
+ ```
50
+
51
+ # What about HTTP/2?
52
+
53
+ It would be a nice exercise in converting a callback-based API to a blocking
54
+ one:
55
+
56
+ ```ruby
57
+ parser = Tipi::HTTP2::Parser.new(socket)
58
+ parser.each_stream(socket) do |stream|
59
+ spin { handle_stream(stream) }
60
+ end
61
+ ```
62
+
63
+
64
+
65
+ # DF
66
+
67
+ - Add attack protection for IP-address HTTP host:
68
+
69
+ ```ruby
70
+ IPV4_REGEXP = /^\d+\.\d+\.\d+\.\d+$/.freeze
71
+
72
+ def is_attack_request?(req)
73
+ return true if req.host =~ IPV4_REGEXP && req.query[:q] != 'ping'
74
+ end
75
+ ```
76
+
77
+ - Add attack route to Qeweney routing API
2
78
 
3
- Problems to fix:
4
79
 
5
- - Memory leak (in server? multi agent? multi client?)
6
80
 
7
81
  # Roadmap
8
82
 
@@ -14,7 +88,7 @@ Problems to fix:
14
88
  - https://gitlab.com/honeyryderchuck/http-2-next
15
89
  - Open an issue there, ask what's the difference between the two gems?
16
90
 
17
- ## 0.35
91
+ ## 0.38
18
92
 
19
93
  - Add more poly CLI commands and options:
20
94
 
@@ -26,7 +100,7 @@ Problems to fix:
26
100
  - set port to bind to
27
101
  - set forking process count
28
102
 
29
- ## 0.36 Working Sinatra application
103
+ ## 0.39 Working Sinatra application
30
104
 
31
105
  - app with database access (postgresql)
32
106
  - benchmarks!
data/df/sample_agent.rb CHANGED
@@ -13,7 +13,7 @@ class SampleAgent < DigitalFabric::Agent
13
13
  HTML_SSE = IO.read(File.join(__dir__, 'sse_page.html'))
14
14
 
15
15
  def http_request(req)
16
- path = req['headers'][':path']
16
+ path = req.headers[':path']
17
17
  case path
18
18
  when '/agent'
19
19
  send_df_message(Protocol.http_response(
data/df/server.rb CHANGED
@@ -6,6 +6,8 @@ require 'tipi/digital_fabric'
6
6
  require 'tipi/digital_fabric/executive'
7
7
  require 'json'
8
8
  require 'fileutils'
9
+ require 'localhost/authority'
10
+
9
11
  FileUtils.cd(__dir__)
10
12
 
11
13
  service = DigitalFabric::Service.new(token: 'foobar')
@@ -19,13 +21,46 @@ end
19
21
 
20
22
  puts "pid: #{Process.pid}"
21
23
 
22
- tcp_listener = spin do
24
+ http_listener = spin do
23
25
  opts = {
24
26
  reuse_addr: true,
25
27
  dont_linger: true,
26
28
  }
27
- puts 'Listening on localhost:4411'
28
- server = Polyphony::Net.tcp_listen('0.0.0.0', 4411, opts)
29
+ puts 'Listening for HTTP on localhost:10080'
30
+ server = Polyphony::Net.tcp_listen('0.0.0.0', 10080, opts)
31
+ server.accept_loop do |client|
32
+ spin do
33
+ service.incr_connection_count
34
+ Tipi.client_loop(client, opts) { |req| service.http_request(req) }
35
+ ensure
36
+ service.decr_connection_count
37
+ end
38
+ rescue Exception => e
39
+ puts "HTTP accept_loop error: #{e.inspect}"
40
+ puts e.backtrace.join("\n")
41
+ end
42
+ end
43
+
44
+ CERTIFICATE_REGEXP = /(-----BEGIN CERTIFICATE-----\n[^-]+-----END CERTIFICATE-----\n)/.freeze
45
+
46
+ https_listener = spin do
47
+ private_key = OpenSSL::PKey::RSA.new IO.read('../../reality/ssl/privkey.pem')
48
+ c = IO.read('../../reality/ssl/cacert.pem')
49
+ certificates = c.scan(CERTIFICATE_REGEXP).map { |p| OpenSSL::X509::Certificate.new(p.first) }
50
+ ctx = OpenSSL::SSL::SSLContext.new
51
+ cert = certificates.shift
52
+ puts "Certificate expires: #{cert.not_after.inspect}"
53
+ ctx.add_certificate(cert, private_key, certificates)
54
+ # ctx = Localhost::Authority.fetch.server_context
55
+ opts = {
56
+ reuse_addr: true,
57
+ dont_linger: true,
58
+ secure_context: ctx,
59
+ alpn_protocols: Tipi::ALPN_PROTOCOLS
60
+ }
61
+
62
+ puts 'Listening for HTTPS on localhost:10443'
63
+ server = Polyphony::Net.tcp_listen('0.0.0.0', 10443, opts)
29
64
  server.accept_loop do |client|
30
65
  spin do
31
66
  service.incr_connection_count
@@ -33,6 +68,9 @@ tcp_listener = spin do
33
68
  ensure
34
69
  service.decr_connection_count
35
70
  end
71
+ rescue Exception => e
72
+ puts "HTTPS accept_loop error: #{e.inspect}"
73
+ puts e.backtrace.join("\n")
36
74
  end
37
75
  end
38
76
 
@@ -46,9 +84,13 @@ unix_listener = spin do
46
84
  end
47
85
 
48
86
  begin
49
- Fiber.await(tcp_listener, unix_listener)
87
+ Fiber.await(http_listener, https_listener, unix_listener)
50
88
  rescue Interrupt
51
89
  puts "Got SIGINT, shutting down gracefully"
52
90
  service.graceful_shutdown
53
91
  puts "post graceful shutdown"
92
+ rescue Exception => e
93
+ puts '*' * 40
94
+ p e
95
+ puts e.backtrace.join("\n")
54
96
  end
data/e ADDED
File without changes
@@ -27,7 +27,8 @@ puts 'Listening on port 4411...'
27
27
 
28
28
  Tipi.serve('0.0.0.0', 4411, opts) do |req|
29
29
  if req.upgrade_protocol == 'websocket'
30
- ws_handler(Tipi::Websocket.new(req.adapter.conn, req.headers))
30
+ conn = req.upgrade_to_websocket
31
+ ws_handler(conn)
31
32
  else
32
33
  req.respond(HTML, 'Content-Type' => 'text/html')
33
34
  end
@@ -13,9 +13,17 @@ puts 'Listening on port 4411...'
13
13
 
14
14
  spin do
15
15
  Tipi.serve('0.0.0.0', 4411, opts) do |req|
16
- req.respond("Hello world!\n")
17
- rescue Exception => e
18
- p e
16
+ if req.path == '/stream'
17
+ req.send_headers('Foo' => 'Bar')
18
+ sleep 1
19
+ req.send_chunk("foo\n")
20
+ sleep 1
21
+ req.send_chunk("bar\n")
22
+ req.finish
23
+ else
24
+ req.respond("Hello world!\n")
25
+ end
26
+ p req.transfer_counts
19
27
  end
20
28
  p 'done...'
21
29
  end.await
@@ -25,4 +25,6 @@ end
25
25
 
26
26
  puts 'Listening on port 1234'
27
27
 
28
+ trap('SIGINT') { exit! }
29
+
28
30
  child_pids.each { |pid| Thread.current.backend.waitpid(pid) }
@@ -3,9 +3,9 @@
3
3
  require 'bundler/setup'
4
4
  require 'tipi'
5
5
 
6
- $throttler = throttle(1000)
6
+ $throttler = Polyphony::Throttler.new(1000)
7
7
  opts = { reuse_addr: true, dont_linger: true }
8
- spin do
8
+ server = spin do
9
9
  Tipi.serve('0.0.0.0', 1234, opts) do |req|
10
10
  $throttler.call { req.respond("Hello world!\n") }
11
11
  end
@@ -13,3 +13,4 @@ end
13
13
 
14
14
  puts "pid: #{Process.pid}"
15
15
  puts 'Listening on port 1234...'
16
+ server.await
@@ -16,7 +16,16 @@ opts = {
16
16
  puts "pid: #{Process.pid}"
17
17
  puts 'Listening on port 1234...'
18
18
  Tipi.serve('0.0.0.0', 1234, opts) do |req|
19
- req.respond("Hello world!\n")
19
+ p path: req.path
20
+ if req.path == '/stream'
21
+ req.send_headers('Foo' => 'Bar')
22
+ sleep 0.5
23
+ req.send_chunk("foo\n")
24
+ sleep 0.5
25
+ req.send_chunk("bar\n", done: true)
26
+ else
27
+ req.respond("Hello world!\n")
28
+ end
20
29
  # req.send_headers
21
30
  # req.send_chunk("Method: #{req.method}\n")
22
31
  # req.send_chunk("Path: #{req.path}\n")