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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'tipi'
5
+ require 'tipi/websocket'
5
6
  require 'localhost/authority'
6
7
 
7
8
  def ws_handler(conn)
@@ -26,7 +27,7 @@ opts = {
26
27
  dont_linger: true,
27
28
  secure_context: authority.server_context,
28
29
  upgrade: {
29
- websocket: Polyphony::Websocket.handler(&method(:ws_handler))
30
+ websocket: Tipi::Websocket.handler(&method(:ws_handler))
30
31
  }
31
32
  }
32
33
 
@@ -4,6 +4,11 @@ require 'bundler/setup'
4
4
  require 'tipi'
5
5
 
6
6
  app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
7
+ unless File.file?(app_path)
8
+ STDERR.puts "Please provide rack config file (there are some in the examples directory.)"
9
+ exit!
10
+ end
11
+
7
12
  app = Tipi::RackAdapter.load(app_path)
8
13
  opts = { reuse_addr: true, dont_linger: true }
9
14
 
@@ -5,7 +5,7 @@ require 'tipi'
5
5
  require 'localhost/authority'
6
6
 
7
7
  app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
8
- app = Polyphony::HTTP::Server::RackAdapter.load(app_path)
8
+ app = Tipi::RackAdapter.load(app_path)
9
9
 
10
10
  authority = Localhost::Authority.fetch
11
11
  opts = {
@@ -5,15 +5,16 @@ require 'tipi'
5
5
  require 'localhost/authority'
6
6
 
7
7
  app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
8
- app = Polyphony::HTTP::Server::RackAdapter.load(app_path)
8
+ app = Tipi::RackAdapter.load(app_path)
9
9
 
10
10
  authority = Localhost::Authority.fetch
11
11
  opts = {
12
12
  reuse_addr: true,
13
+ reuse_port: true,
13
14
  dont_linger: true,
14
15
  secure_context: authority.server_context
15
16
  }
16
- server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
17
+ server = Tipi.listen('0.0.0.0', 1234, opts)
17
18
  puts 'Listening on port 1234'
18
19
 
19
20
  child_pids = []
@@ -24,4 +25,4 @@ child_pids = []
24
25
  end
25
26
  end
26
27
 
27
- child_pids.each { |pid| EV::Child.new(pid).await }
28
+ child_pids.each { |pid| Thread.current.backend.waitpid(pid) }
@@ -12,17 +12,18 @@ puts "pid: #{Process.pid}"
12
12
  puts 'Listening on port 4411...'
13
13
 
14
14
  app = Tipi.route do |r|
15
- r.root do
15
+
16
+ r.on_root do
16
17
  r.redirect '/hello'
17
18
  end
18
19
  r.on 'hello' do
19
- r.get 'world' do
20
+ r.on_get 'world' do
20
21
  r.respond 'Hello world'
21
22
  end
22
- r.get do
23
+ r.on_get do
23
24
  r.respond 'Hello'
24
25
  end
25
- r.post do
26
+ r.on_post do
26
27
  puts 'Someone said Hello'
27
28
  r.redirect '/'
28
29
  end
@@ -119,7 +119,7 @@ module DigitalFabric
119
119
 
120
120
  def recv_df_message(msg)
121
121
  @last_recv = Time.now
122
- case msg['kind']
122
+ case msg[Protocol::Attribute::KIND]
123
123
  when Protocol::SHUTDOWN
124
124
  recv_shutdown
125
125
  when Protocol::HTTP_REQUEST
@@ -130,7 +130,7 @@ module DigitalFabric
130
130
  recv_ws_request(msg)
131
131
  when Protocol::CONN_DATA, Protocol::CONN_CLOSE,
132
132
  Protocol::WS_DATA, Protocol::WS_CLOSE
133
- fiber = @requests[msg['id']]
133
+ fiber = @requests[msg[Protocol::Attribute::ID]]
134
134
  fiber << msg if fiber
135
135
  end
136
136
  end
@@ -140,7 +140,7 @@ module DigitalFabric
140
140
  # messages. This is so we can correctly stop long-running requests
141
141
  # upon graceful shutdown
142
142
  if is_long_running_request_response?(msg)
143
- id = msg[:id]
143
+ id = msg[Protocol::Attribute::ID]
144
144
  @long_running_requests[id] = @requests[id]
145
145
  end
146
146
  @last_send = Time.now
@@ -148,11 +148,11 @@ module DigitalFabric
148
148
  end
149
149
 
150
150
  def is_long_running_request_response?(msg)
151
- case msg[:kind]
151
+ case msg[Protocol::Attribute::KIND]
152
152
  when Protocol::HTTP_UPGRADE
153
153
  true
154
154
  when Protocol::HTTP_RESPONSE
155
- msg[:body] && !msg[:complete]
155
+ !msg[Protocol::Attribute::HttpResponse::COMPLETE]
156
156
  end
157
157
  end
158
158
 
@@ -165,7 +165,7 @@ module DigitalFabric
165
165
 
166
166
  def recv_http_request(msg)
167
167
  req = prepare_http_request(msg)
168
- id = msg['id']
168
+ id = msg[Protocol::Attribute::ID]
169
169
  @requests[id] = spin do
170
170
  http_request(req)
171
171
  rescue IOError, Errno::ECONNREFUSED, Errno::EPIPE
@@ -180,17 +180,20 @@ module DigitalFabric
180
180
  end
181
181
 
182
182
  def prepare_http_request(msg)
183
- req = Qeweney::Request.new(msg['headers'], RequestAdapter.new(self, msg))
184
- req.buffer_body_chunk(msg['body']) if msg['body']
185
- req.complete! if msg['complete']
183
+ headers = msg[Protocol::Attribute::HttpRequest::HEADERS]
184
+ body_chunk = msg[Protocol::Attribute::HttpRequest::BODY_CHUNK]
185
+ complete = msg[Protocol::Attribute::HttpRequest::COMPLETE]
186
+ req = Qeweney::Request.new(headers, RequestAdapter.new(self, msg))
187
+ req.buffer_body_chunk(body_chunk) if body_chunk
188
+ req.complete! if complete
186
189
  req
187
190
  end
188
191
 
189
192
  def recv_http_request_body(msg)
190
- fiber = @requests[msg['id']]
193
+ fiber = @requests[msg[Protocol::Attribute::ID]]
191
194
  return unless fiber
192
195
 
193
- fiber << msg['body']
196
+ fiber << msg[Protocol::Attribute::HttpRequestBody::BODY]
194
197
  end
195
198
 
196
199
  def get_http_request_body(id, limit)
@@ -199,8 +202,8 @@ module DigitalFabric
199
202
  end
200
203
 
201
204
  def recv_ws_request(msg)
202
- req = Qeweney::Request.new(msg['headers'], RequestAdapter.new(self, msg))
203
- id = msg['id']
205
+ req = Qeweney::Request.new(msg[Protocol::Attribute::WS::HEADERS], RequestAdapter.new(self, msg))
206
+ id = msg[Protocol::Attribute::ID]
204
207
  @requests[id] = @long_running_requests[id] = spin do
205
208
  ws_request(req)
206
209
  rescue IOError, Errno::ECONNREFUSED, Errno::EPIPE
@@ -31,6 +31,7 @@ module DigitalFabric
31
31
  def run
32
32
  @fiber = Fiber.current
33
33
  @service.mount(route, self)
34
+ @mounted = true
34
35
  keep_alive_timer = spin_loop(interval: 5) { keep_alive }
35
36
  process_incoming_messages(false)
36
37
  rescue GracefulShutdown
@@ -38,7 +39,7 @@ module DigitalFabric
38
39
  process_incoming_messages(true)
39
40
  ensure
40
41
  keep_alive_timer&.stop
41
- @service.unmount(self)
42
+ unmount
42
43
  end
43
44
 
44
45
  def process_incoming_messages(shutdown = false)
@@ -48,7 +49,15 @@ module DigitalFabric
48
49
  recv_df_message(msg)
49
50
  return if shutdown && @requests.empty?
50
51
  end
51
- rescue TimeoutError, IOError
52
+ rescue TimeoutError, IOError, SystemCallError
53
+ # ignore and just return in order to terminate the proxy
54
+ end
55
+
56
+ def unmount
57
+ return unless @mounted
58
+
59
+ @service.unmount(self)
60
+ @mounted = nil
52
61
  end
53
62
 
54
63
  def shutdown
@@ -82,9 +91,16 @@ module DigitalFabric
82
91
 
83
92
  def recv_df_message(message)
84
93
  @last_recv = Time.now
85
- return if message['kind'] == Protocol::PING
94
+ # puts "<<< #{message.inspect}"
86
95
 
87
- handler = @requests[message['id']]
96
+ case message[Protocol::Attribute::KIND]
97
+ when Protocol::PING
98
+ return
99
+ when Protocol::UNMOUNT
100
+ return unmount
101
+ end
102
+
103
+ handler = @requests[message[Protocol::Attribute::ID]]
88
104
  if !handler
89
105
  # puts "Unknown request id in #{message}"
90
106
  return
@@ -94,6 +110,8 @@ module DigitalFabric
94
110
  end
95
111
 
96
112
  def send_df_message(message)
113
+ # puts ">>> #{message.inspect}" unless message[Protocol::Attribute::KIND] == Protocol::PING
114
+
97
115
  @last_send = Time.now
98
116
  @conn << message.to_msgpack
99
117
  end
@@ -130,41 +148,51 @@ module DigitalFabric
130
148
  t1 = Time.now
131
149
  @service.record_latency_measurement(t1 - t0)
132
150
  end
133
- return if http_request_message(id, req, message)
151
+ kind = message[Protocol::Attribute::KIND]
152
+ attributes = message[Protocol::Attribute::HttpRequest::HEADERS..-1]
153
+ return if http_request_message(id, req, kind, attributes)
134
154
  end
135
155
  end
136
156
  rescue => e
137
- req.respond("Error: #{e.inspect}", ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
157
+ p "Internal server error: #{e.inspect}"
158
+ puts e.backtrace.join("\n")
159
+ http_request_send_error_response(e)
160
+ end
161
+
162
+ def http_request_send_error_response(error)
163
+ response = format("Error: %s\n%s", error.inspect, error.backtrace.join("\n"))
164
+ req.respond(response, ':status' => Qeweney::Status::INTERNAL_SERVER_ERROR)
165
+ rescue IOError, SystemCallError
166
+ # ignore
138
167
  end
139
168
 
140
169
  # @return [Boolean] true if response is complete
141
- def http_request_message(id, req, message)
142
- case message['kind']
170
+ def http_request_message(id, req, kind, message)
171
+ case kind
143
172
  when Protocol::HTTP_UPGRADE
144
- http_custom_upgrade(id, req, message)
173
+ http_custom_upgrade(id, req, *message)
145
174
  true
146
175
  when Protocol::HTTP_GET_REQUEST_BODY
147
- http_get_request_body(id, req, message)
176
+ http_get_request_body(id, req, *message)
148
177
  false
149
178
  when Protocol::HTTP_RESPONSE
150
- headers = message['headers']
151
- body = message['body']
152
- done = message['complete']
153
- req.send_headers(headers) if headers && !req.headers_sent?
154
- req.send_chunk(body, done: done) if body or done
155
- done
179
+ http_response(id, req, *message)
156
180
  else
157
181
  # invalid message
158
182
  true
159
183
  end
160
184
  end
161
185
 
186
+ def send_transfer_count(key, rx, tx)
187
+ send_df_message(Protocol.transfer_count(key, rx, tx))
188
+ end
189
+
162
190
  HTTP_RESPONSE_UPGRADE_HEADERS = { ':status' => Qeweney::Status::SWITCHING_PROTOCOLS }
163
191
 
164
- def http_custom_upgrade(id, req, message)
192
+ def http_custom_upgrade(id, req, headers)
165
193
  # send upgrade response
166
- upgrade_headers = message['headers'] ?
167
- message['headers'].merge(HTTP_RESPONSE_UPGRADE_HEADERS) :
194
+ upgrade_headers = headers ?
195
+ headers.merge(HTTP_RESPONSE_UPGRADE_HEADERS) :
168
196
  HTTP_RESPONSE_UPGRADE_HEADERS
169
197
  req.send_headers(upgrade_headers, true)
170
198
 
@@ -182,9 +210,9 @@ module DigitalFabric
182
210
  end
183
211
 
184
212
  def http_custom_upgrade_message(conn, message)
185
- case message['kind']
213
+ case message[Protocol::Attribute::KIND]
186
214
  when Protocol::CONN_DATA
187
- conn << message['data']
215
+ conn << message[:Protocol::Attribute::ConnData::DATA]
188
216
  false
189
217
  when Protocol::CONN_CLOSE
190
218
  true
@@ -194,8 +222,30 @@ module DigitalFabric
194
222
  end
195
223
  end
196
224
 
197
- def http_get_request_body(id, req, message)
198
- case (limit = message['limit'])
225
+ def http_response(id, req, body, headers, complete, transfer_count_key)
226
+ if !req.headers_sent? && complete
227
+ req.respond(body, headers|| {})
228
+ if transfer_count_key
229
+ rx, tx = req.transfer_counts
230
+ send_transfer_count(transfer_count_key, rx, tx)
231
+ end
232
+ true
233
+ else
234
+ req.send_headers(headers) if headers && !req.headers_sent?
235
+ req.send_chunk(body, done: complete) if body or complete
236
+
237
+ if complete && transfer_count_key
238
+ rx, tx = req.transfer_counts
239
+ send_transfer_count(transfer_count_key, rx, tx)
240
+ end
241
+ complete
242
+ end
243
+ rescue IOError, SystemCallError
244
+ # ignore error
245
+ end
246
+
247
+ def http_get_request_body(id, req, limit)
248
+ case limit
199
249
  when nil
200
250
  body = req.read
201
251
  else
@@ -225,9 +275,9 @@ module DigitalFabric
225
275
  with_request do |id|
226
276
  send_df_message(Protocol.ws_request(id, req.headers))
227
277
  response = receive
228
- case response['kind']
278
+ case response[0]
229
279
  when Protocol::WS_RESPONSE
230
- headers = response['headers'] || {}
280
+ headers = response[2] || {}
231
281
  status = headers[':status'] || Qeweney::Status::SWITCHING_PROTOCOLS
232
282
  if status != Qeweney::Status::SWITCHING_PROTOCOLS
233
283
  req.respond(nil, headers)
@@ -239,6 +289,8 @@ module DigitalFabric
239
289
  req.respond(nil, ':status' => Qeweney::Status::SERVICE_UNAVAILABLE)
240
290
  end
241
291
  end
292
+ rescue IOError, SystemCallError
293
+ # ignore
242
294
  end
243
295
 
244
296
  def run_websocket_connection(id, websocket)
@@ -248,9 +300,9 @@ module DigitalFabric
248
300
  end
249
301
  end
250
302
  while (message = receive)
251
- case message['kind']
303
+ case message[Protocol::Attribute::KIND]
252
304
  when Protocol::WS_DATA
253
- websocket << message['data']
305
+ websocket << message[Protocol::Attribute::WS::DATA]
254
306
  when Protocol::WS_CLOSE
255
307
  return
256
308
  else
@@ -4,6 +4,7 @@ module DigitalFabric
4
4
  module Protocol
5
5
  PING = 'ping'
6
6
  SHUTDOWN = 'shutdown'
7
+ UNMOUNT = 'unmount'
7
8
 
8
9
  HTTP_REQUEST = 'http_request'
9
10
  HTTP_RESPONSE = 'http_response'
@@ -19,16 +20,68 @@ module DigitalFabric
19
20
  WS_DATA = 'ws_data'
20
21
  WS_CLOSE = 'ws_close'
21
22
 
23
+ TRANSFER_COUNT = 'transfer_count'
24
+
22
25
  SEND_TIMEOUT = 15
23
26
  RECV_TIMEOUT = SEND_TIMEOUT + 5
24
27
 
28
+ module Attribute
29
+ KIND = 0
30
+ ID = 1
31
+
32
+ module HttpRequest
33
+ HEADERS = 2
34
+ BODY_CHUNK = 3
35
+ COMPLETE = 4
36
+ end
37
+
38
+ module HttpResponse
39
+ BODY = 2
40
+ HEADERS = 3
41
+ COMPLETE = 4
42
+ TRANSFER_COUNT_KEY = 5
43
+ end
44
+
45
+ module HttpUpgrade
46
+ HEADERS = 2
47
+ end
48
+
49
+ module HttpGetRequestBody
50
+ LIMIT = 2
51
+ end
52
+
53
+ module HttpRequestBody
54
+ BODY = 2
55
+ COMPLETE = 3
56
+ end
57
+
58
+ module ConnectionData
59
+ DATA = 2
60
+ end
61
+
62
+ module WS
63
+ HEADERS = 2
64
+ DATA = 2
65
+ end
66
+
67
+ module TransferCount
68
+ KEY = 1
69
+ RX = 2
70
+ TX = 3
71
+ end
72
+ end
73
+
25
74
  class << self
26
75
  def ping
27
- { kind: PING }
76
+ [ PING ]
28
77
  end
29
78
 
30
79
  def shutdown
31
- { kind: SHUTDOWN }
80
+ [ SHUTDOWN ]
81
+ end
82
+
83
+ def unmount
84
+ [ UNMOUNT ]
32
85
  end
33
86
 
34
87
  DF_UPGRADE_RESPONSE = <<~HTTP.gsub("\n", "\r\n")
@@ -43,47 +96,51 @@ module DigitalFabric
43
96
  end
44
97
 
45
98
  def http_request(id, req)
46
- { kind: HTTP_REQUEST, id: id, headers: req.headers, body: req.next_chunk, complete: req.complete? }
99
+ [ HTTP_REQUEST, id, req.headers, req.next_chunk, req.complete? ]
47
100
  end
48
101
 
49
- def http_response(id, body, headers, complete)
50
- { kind: HTTP_RESPONSE, id: id, body: body, headers: headers, complete: complete }
102
+ def http_response(id, body, headers, complete, transfer_count_key = nil)
103
+ [ HTTP_RESPONSE, id, body, headers, complete, transfer_count_key ]
51
104
  end
52
105
 
53
106
  def http_upgrade(id, headers)
54
- { kind: HTTP_UPGRADE, id: id }
107
+ [ HTTP_UPGRADE, id, headers ]
55
108
  end
56
109
 
57
110
  def http_get_request_body(id, limit = nil)
58
- { kind: HTTP_GET_REQUEST_BODY, id: id, limit: limit }
111
+ [ HTTP_GET_REQUEST_BODY, id, limit ]
59
112
  end
60
113
 
61
114
  def http_request_body(id, body, complete)
62
- { kind: HTTP_REQUEST_BODY, id: id, body: body, complete: complete }
115
+ [ HTTP_REQUEST_BODY, id, body, complete ]
63
116
  end
64
117
 
65
118
  def connection_data(id, data)
66
- { kind: CONN_DATA, id: id, data: data }
119
+ [ CONN_DATA, id, data ]
67
120
  end
68
121
 
69
122
  def connection_close(id)
70
- { kind: CONN_CLOSE, id: id }
123
+ [ CONN_CLOSE, id ]
71
124
  end
72
125
 
73
126
  def ws_request(id, headers)
74
- { kind: WS_REQUEST, id: id, headers: headers }
127
+ [ WS_REQUEST, id, headers ]
75
128
  end
76
129
 
77
130
  def ws_response(id, headers)
78
- { kind: WS_RESPONSE, id: id, headers: headers }
131
+ [ WS_RESPONSE, id, headers ]
79
132
  end
80
133
 
81
134
  def ws_data(id, data)
82
- { id: id, kind: WS_DATA, data: data }
135
+ [ WS_DATA, id, data ]
83
136
  end
84
137
 
85
138
  def ws_close(id)
86
- { id: id, kind: WS_CLOSE }
139
+ [WS_CLOSE, id ]
140
+ end
141
+
142
+ def transfer_count(key, rx, tx)
143
+ [ TRANSFER_COUNT, key, rx, tx ]
87
144
  end
88
145
  end
89
146
  end