tipi 0.36 → 0.39

Sign up to get free protection for your applications and to get access to all the features.
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")