tipi 0.38 → 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.
@@ -16,7 +16,9 @@ module Tipi
16
16
  @opts = opts
17
17
  @upgrade_headers = upgrade_headers
18
18
  @first = true
19
-
19
+ @rx = (upgrade_headers && upgrade_headers[':rx']) || 0
20
+ @tx = (upgrade_headers && upgrade_headers[':tx']) || 0
21
+
20
22
  @interface = ::HTTP2::Server.new
21
23
  @connection_fiber = Fiber.current
22
24
  @interface.on(:frame, &method(:send_frame))
@@ -24,6 +26,9 @@ module Tipi
24
26
  end
25
27
 
26
28
  def send_frame(data)
29
+ if @transfer_count_request
30
+ @transfer_count_request.tx_incr(data.bytesize)
31
+ end
27
32
  @conn << data
28
33
  rescue Exception => e
29
34
  @connection_fiber.transfer e
@@ -38,6 +43,7 @@ module Tipi
38
43
 
39
44
  def upgrade
40
45
  @conn << UPGRADE_MESSAGE
46
+ @tx += UPGRADE_MESSAGE.bytesize
41
47
  settings = @upgrade_headers['http2-settings']
42
48
  Fiber.current.schedule(nil)
43
49
  @interface.upgrade(settings, @upgrade_headers, '')
@@ -49,16 +55,31 @@ module Tipi
49
55
  def each(&block)
50
56
  @interface.on(:stream) { |stream| start_stream(stream, &block) }
51
57
  upgrade if @upgrade_headers
52
-
53
- @conn.recv_loop(&@interface.method(:<<))
58
+
59
+ @conn.recv_loop do |data|
60
+ @rx += data.bytesize
61
+ @interface << data
62
+ end
54
63
  rescue SystemCallError, IOError
55
64
  # ignore
56
65
  ensure
57
66
  finalize_client_loop
58
67
  end
68
+
69
+ def get_rx_count
70
+ count = @rx
71
+ @rx = 0
72
+ count
73
+ end
74
+
75
+ def get_tx_count
76
+ count = @tx
77
+ @tx = 0
78
+ count
79
+ end
59
80
 
60
81
  def start_stream(stream, &block)
61
- stream = HTTP2StreamHandler.new(stream, @conn, @first, &block)
82
+ stream = HTTP2StreamHandler.new(self, stream, @conn, @first, &block)
62
83
  @first = nil if @first
63
84
  @streams[stream] = true
64
85
  end
@@ -72,5 +93,15 @@ module Tipi
72
93
  def close
73
94
  @conn.close
74
95
  end
96
+
97
+ def set_request_for_transfer_count(request)
98
+ @transfer_count_request = request
99
+ end
100
+
101
+ def unset_request_for_transfer_count(request)
102
+ return unless @transfer_count_request == request
103
+
104
+ @transfer_count_request = nil
105
+ end
75
106
  end
76
107
  end
@@ -9,13 +9,15 @@ module Tipi
9
9
  attr_accessor :__next__
10
10
  attr_reader :conn
11
11
 
12
- def initialize(stream, conn, first, &block)
12
+ def initialize(adapter, stream, conn, first, &block)
13
+ @adapter = adapter
13
14
  @stream = stream
14
15
  @conn = conn
15
16
  @first = first
16
17
  @connection_fiber = Fiber.current
17
18
  @stream_fiber = spin { |req| handle_request(req, &block) }
18
-
19
+ Thread.current.fiber_unschedule(@stream_fiber)
20
+
19
21
  # Stream callbacks occur on the connection fiber (see HTTP2Adapter#each).
20
22
  # The request handler is run on a separate fiber for each stream, allowing
21
23
  # concurrent handling of incoming requests on the same HTTP/2 connection.
@@ -47,14 +49,17 @@ module Tipi
47
49
 
48
50
  def on_headers(headers)
49
51
  @request = Qeweney::Request.new(headers.to_h, self)
52
+ @request.rx_incr(@adapter.get_rx_count)
53
+ @request.tx_incr(@adapter.get_tx_count)
50
54
  if @first
51
55
  @request.headers[':first'] = true
52
56
  @first = false
53
57
  end
54
58
  @stream_fiber.schedule @request
55
59
  end
56
-
60
+
57
61
  def on_data(data)
62
+ data = data.to_s # chunks might be wrapped in a HTTP2::Buffer
58
63
  if @waiting_for_body_chunk
59
64
  @waiting_for_body_chunk = nil
60
65
  @stream_fiber.schedule data
@@ -78,62 +83,100 @@ module Tipi
78
83
  def protocol
79
84
  'h2'
80
85
  end
86
+
87
+ def with_transfer_count(request)
88
+ @adapter.set_request_for_transfer_count(request)
89
+ yield
90
+ ensure
91
+ @adapter.unset_request_for_transfer_count(request)
92
+ end
81
93
 
82
- def get_body_chunk
94
+ def get_body_chunk(request)
83
95
  # called in the context of the stream fiber
84
96
  return nil if @request.complete?
85
97
 
86
- @waiting_for_body_chunk = true
87
- # the chunk (or an exception) will be returned once the stream fiber is
88
- # resumed
89
- suspend
98
+ with_transfer_count(request) do
99
+ @waiting_for_body_chunk = true
100
+ # the chunk (or an exception) will be returned once the stream fiber is
101
+ # resumed
102
+ suspend
103
+ end
90
104
  ensure
91
105
  @waiting_for_body_chunk = nil
92
106
  end
93
107
 
94
108
  # Wait for request to finish
95
- def consume_request
109
+ def consume_request(request)
96
110
  return if @request.complete?
97
111
 
98
- @waiting_for_half_close = true
99
- suspend
112
+ with_transfer_count(request) do
113
+ @waiting_for_half_close = true
114
+ suspend
115
+ end
100
116
  ensure
101
117
  @waiting_for_half_close = nil
102
118
  end
103
119
 
104
120
  # response API
105
- def respond(chunk, headers)
121
+ def respond(request, chunk, headers)
106
122
  headers[':status'] ||= Qeweney::Status::OK
107
- @stream.headers(headers, end_stream: false)
108
- @stream.data(chunk, end_stream: true)
123
+ headers[':status'] = headers[':status'].to_s
124
+ with_transfer_count(request) do
125
+ @stream.headers(transform_headers(headers))
126
+ @stream.data(chunk || '')
127
+ end
109
128
  @headers_sent = true
129
+ rescue HTTP2::Error::StreamClosed
130
+ # ignore
131
+ end
132
+
133
+ def transform_headers(headers)
134
+ headers.each_with_object([]) do |(k, v), a|
135
+ if v.is_a?(Array)
136
+ v.each { |vv| a << [k, vv.to_s] }
137
+ else
138
+ a << [k, v.to_s]
139
+ end
140
+ end
110
141
  end
111
142
 
112
- def send_headers(headers, empty_response = false)
143
+ def send_headers(request, headers, empty_response: false)
113
144
  return if @headers_sent
114
145
 
115
146
  headers[':status'] ||= (empty_response ? Qeweney::Status::NO_CONTENT : Qeweney::Status::OK).to_s
116
- @stream.headers(headers, end_stream: false)
147
+ with_transfer_count(request) do
148
+ @stream.headers(transform_headers(headers), end_stream: false)
149
+ end
117
150
  @headers_sent = true
151
+ rescue HTTP2::Error::StreamClosed
152
+ # ignore
118
153
  end
119
154
 
120
- def send_chunk(chunk, done: false)
155
+ def send_chunk(request, chunk, done: false)
121
156
  send_headers({}, false) unless @headers_sent
122
157
 
123
158
  if chunk
124
- @stream.data(chunk, end_stream: done)
159
+ with_transfer_count(request) do
160
+ @stream.data(chunk, end_stream: done)
161
+ end
125
162
  elsif done
126
163
  @stream.close
127
164
  end
165
+ rescue HTTP2::Error::StreamClosed
166
+ # ignore
128
167
  end
129
168
 
130
- def finish
169
+ def finish(request)
131
170
  if @headers_sent
132
171
  @stream.close
133
172
  else
134
173
  headers[':status'] ||= Qeweney::Status::NO_CONTENT
135
- @stream.headers(headers, end_stream: true)
174
+ with_transfer_count(request) do
175
+ @stream.headers(transform_headers(headers), end_stream: true)
176
+ end
136
177
  end
178
+ rescue HTTP2::Error::StreamClosed
179
+ # ignore
137
180
  end
138
181
 
139
182
  def stop
data/lib/tipi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tipi
4
- VERSION = '0.38'
4
+ VERSION = '0.39'
5
5
  end
data/tipi.gemspec CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.executables = ['tipi']
21
21
 
22
- s.add_runtime_dependency 'polyphony', '~>0.52.0'
23
- s.add_runtime_dependency 'qeweney', '~>0.6'
22
+ s.add_runtime_dependency 'polyphony', '~>0.55.0'
23
+ s.add_runtime_dependency 'qeweney', '~>0.9.1'
24
24
 
25
25
  s.add_runtime_dependency 'http_parser.rb', '~>0.6.0'
26
26
  s.add_runtime_dependency 'http-2', '~>0.10.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tipi
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.38'
4
+ version: '0.39'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-09 00:00:00.000000000 Z
11
+ date: 2021-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: polyphony
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.52.0
19
+ version: 0.55.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.52.0
26
+ version: 0.55.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: qeweney
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.6'
33
+ version: 0.9.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.6'
40
+ version: 0.9.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: http_parser.rb
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -292,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
292
292
  - !ruby/object:Gem::Version
293
293
  version: '0'
294
294
  requirements: []
295
- rubygems_version: 3.0.8
295
+ rubygems_version: 3.1.4
296
296
  signing_key:
297
297
  specification_version: 4
298
298
  summary: Tipi - the All-in-one Web Server for Ruby Apps