uv-rays 1.3.8 → 2.0.1

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
  SHA1:
3
- metadata.gz: 9f5437880d127e446a1e89f0160196101c064596
4
- data.tar.gz: 17ee53fe763b9ae8d53caebcfa02c692f80f4da7
3
+ metadata.gz: dcf2dd4cbeb83a76c6f218cc4c0ccc55ed05d755
4
+ data.tar.gz: 2f1e98b94414be0f86f22b5297b38e45c9cc8882
5
5
  SHA512:
6
- metadata.gz: 3596ce722dac73675539be1a0c2336da15b116a5205d13e4ccd86916fbbddeb49c685496acda002db1691e2eb7b91782e4b94a0c3ef1a3132faa364c96fef68c
7
- data.tar.gz: a96be731ac57037fac6a69f43ac169b32e21f70d7e669c7db29a06172688e1b43bb9561fbca983e3ce26d8c3a035c381550307cff3361552c6e71f0a43f6f80f
6
+ metadata.gz: cc24a2d715a2325b8f531bd4823cf73e7ed9e54dcb6ee31eeacc830b3af1df2cd70b4280e68409a61507e5fbfcbf6aa9576e9d59b56f9afe9f07939550759f78
7
+ data.tar.gz: 3be2b91df14b7be0b7fbe7f2c50c08fbaf45ff56e586b50db736a87a12c02c411e27f21852ac2baa0aaee3e7df296cb03d981043c9f7178e6fd509dbd75b1a69
data/README.md CHANGED
@@ -27,26 +27,25 @@ Run `gem install uv-rays` to install
27
27
  Here's a fully-functional echo server written with UV-Rays:
28
28
 
29
29
  ```ruby
30
-
31
30
  require 'uv-rays'
32
31
 
33
32
  module EchoServer
34
- def post_init
35
- puts "-- someone connected to the echo server!"
36
- end
37
-
38
- def on_read data, *args
39
- write ">>>you sent: #{data}"
40
- close_connection if data =~ /quit/i
41
- end
42
-
43
- def on_close
44
- puts "-- someone disconnected from the echo server!"
45
- end
33
+ def on_connect(socket)
34
+ @ip, @port = socket.peername
35
+ logger.info "-- #{@ip}:#{@port} connected"
36
+ end
37
+
38
+ def on_read(data, socket)
39
+ write ">>>you sent: #{data}"
40
+ close_connection if data =~ /quit/i
41
+ end
42
+
43
+ def on_close
44
+ puts "-- #{@ip}:#{@port} disconnected"
45
+ end
46
46
  end
47
47
 
48
- # Note that this will block current thread.
49
- Libuv::Loop.default.run {
48
+ reactor {
50
49
  UV.start_server "127.0.0.1", 8081, EchoServer
51
50
  }
52
51
 
@@ -0,0 +1,80 @@
1
+ require 'faraday'
2
+ require 'uv-rays'
3
+
4
+
5
+ module Faraday
6
+ class Adapter < Middleware
7
+ register_middleware libuv: :Libuv
8
+
9
+ class Libuv < Faraday::Adapter
10
+ def initialize(app, connection_options = {})
11
+ @connection_options = connection_options
12
+ super(app)
13
+ end
14
+
15
+ def call(env)
16
+ super
17
+
18
+ opts = {}
19
+ if env[:url].scheme == 'https' && ssl = env[:ssl]
20
+ tls_opts = opts[:tls_options] = {}
21
+
22
+ # opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true)
23
+ # TODO:: Need to provide verify callbacks
24
+
25
+ tls_opts[:cert_chain] = ssl[:ca_path] if ssl[:ca_path]
26
+ tls_opts[:client_ca] = ssl[:ca_file] if ssl[:ca_file]
27
+ #tls_opts[:client_cert] = ssl[:client_cert] if ssl[:client_cert]
28
+ #tls_opts[:client_key] = ssl[:client_key] if ssl[:client_key]
29
+ #tls_opts[:certificate] = ssl[:certificate] if ssl[:certificate]
30
+ tls_opts[:private_key] = ssl[:private_key] if ssl[:private_key]
31
+ end
32
+
33
+ if (req = env[:request])
34
+ opts[:inactivity_timeout] = req[:timeout] if req[:timeout]
35
+ end
36
+
37
+ error = nil
38
+ thread = reactor
39
+ if thread.running?
40
+ error = perform_request(env, opts)
41
+ else
42
+ # Pretty much here for testing
43
+ thread.run {
44
+ error = perform_request(env, opts)
45
+ }
46
+ end
47
+
48
+ # Re-raise the error out of the event loop
49
+ # Really this is only required for tests as this will always run on the reactor
50
+ raise error if error
51
+ @app.call env
52
+ rescue ::CoroutineRejection => err
53
+ if err.value == :timeout
54
+ raise Error::TimeoutError, err
55
+ else
56
+ raise Error::ConnectionFailed, err
57
+ end
58
+ end
59
+
60
+ # TODO: support streaming requests
61
+ def read_body(env)
62
+ env[:body].respond_to?(:read) ? env[:body].read : env[:body]
63
+ end
64
+ end
65
+
66
+ def perform_request(env, opts)
67
+ conn = ::UV::HttpEndpoint.new(env[:url].to_s, opts.merge!(@connection_options))
68
+ resp = co conn.request(env[:method].to_s.downcase.to_sym,
69
+ headers: env[:request_headers],
70
+ path: "/#{env[:url].to_s.split('/', 4)[-1]}",
71
+ keepalive: false,
72
+ body: read_body(env))
73
+
74
+ save_response(env, resp.status.to_i, resp.body, resp) #, resp.reason_phrase)
75
+ nil
76
+ rescue Exception => e
77
+ e
78
+ end
79
+ end
80
+ end
data/lib/uv-rays.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'libuv'
2
4
 
3
5
 
@@ -65,21 +67,21 @@ module UV
65
67
  end
66
68
 
67
69
  def self.start_server(server, port, handler, *args)
68
- loop = Libuv::Loop.current # Get the current event loop
69
- raise ThreadError, "There is no Libuv Loop running on the current thread" if loop.nil?
70
+ thread = reactor # Get the reactor running on this thread
71
+ raise ThreadError, "There is no Libuv reactor running on the current thread" if thread.nil?
70
72
 
71
73
  klass = klass_from_handler(InboundConnection, handler, *args)
72
- UV::TcpServer.new loop, server, port, klass, *args
74
+ UV::TcpServer.new thread, server, port, klass, *args
73
75
  end
74
76
 
75
77
  def self.attach_server(sock, handler, *args)
76
- loop = Libuv::Loop.current # Get the current event loop
77
- raise ThreadError, "There is no Libuv Loop running on the current thread" if loop.nil?
78
+ thread = reactor # Get the reactor running on this thread
79
+ raise ThreadError, "There is no Libuv reactor running on the current thread" if thread.nil?
78
80
 
79
81
  klass = klass_from_handler(InboundConnection, handler, *args)
80
82
  sd = sock.respond_to?(:fileno) ? sock.fileno : sock
81
83
 
82
- UV::TcpServer.new loop, sd, sd, klass, *args
84
+ UV::TcpServer.new thread, sd, sd, klass, *args
83
85
  end
84
86
 
85
87
  def self.open_datagram_socket(handler, server = nil, port = nil, *args)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
1
2
 
2
3
  module UV
3
4
 
@@ -6,7 +7,7 @@ module UV
6
7
  # callback based system for application level tokenization without
7
8
  # the heavy lifting.
8
9
  class AbstractTokenizer
9
- DEFAULT_ENCODING = 'ASCII-8BIT'.freeze
10
+ DEFAULT_ENCODING = 'ASCII-8BIT'
10
11
 
11
12
  attr_accessor :callback, :indicator, :size_limit, :verbose
12
13
 
@@ -21,9 +22,10 @@ module UV
21
22
  raise ArgumentError, 'no indicator provided' unless @indicator
22
23
  raise ArgumentError, 'no callback provided' unless @callback
23
24
 
24
- @input = ''
25
- @input.force_encoding(@encoding)
26
- @indicator.force_encoding(@encoding) if @indicator.is_a?(String)
25
+ reset
26
+ if @indicator.is_a?(String)
27
+ @indicator = String.new(@indicator).encode(@encoding).freeze
28
+ end
27
29
  end
28
30
 
29
31
  # Extract takes an arbitrary string of input data and returns an array of
@@ -101,7 +103,7 @@ module UV
101
103
 
102
104
 
103
105
  def reset
104
- @input = ''.force_encoding(@encoding)
106
+ @input = String.new.force_encoding(@encoding)
105
107
  end
106
108
  end
107
109
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
1
2
 
2
3
  # BufferedTokenizer takes a delimiter upon instantiation.
3
4
  # It allows input to be spoon-fed from some outside source which receives
@@ -15,7 +16,7 @@
15
16
  # end
16
17
  module UV
17
18
  class BufferedTokenizer
18
- DEFAULT_ENCODING = 'ASCII-8BIT'.freeze
19
+ DEFAULT_ENCODING = 'ASCII-8BIT'
19
20
 
20
21
  attr_accessor :delimiter, :indicator, :size_limit, :verbose
21
22
 
@@ -30,11 +31,8 @@ module UV
30
31
  @encoding = options[:encoding] || DEFAULT_ENCODING
31
32
 
32
33
  if @delimiter
33
- @delimiter.force_encoding(@encoding) if @delimiter.is_a?(String)
34
- @indicator.force_encoding(@encoding) if @indicator.is_a?(String)
35
34
  @extract_method = method(:delimiter_extract)
36
35
  elsif @indicator && @msg_length
37
- @indicator.force_encoding(@encoding) if @indicator.is_a?(String)
38
36
  @extract_method = method(:length_extract)
39
37
  else
40
38
  raise ArgumentError, 'no delimiter provided'
@@ -88,7 +86,7 @@ module UV
88
86
  messages = @input.split(@delimiter, -1)
89
87
 
90
88
  if @indicator
91
- @input = messages.pop || ''
89
+ @input = messages.pop || empty_string
92
90
  entities = []
93
91
  messages.each do |msg|
94
92
  res = msg.split(@indicator, -1)
@@ -96,7 +94,7 @@ module UV
96
94
  end
97
95
  else
98
96
  entities = messages
99
- @input = entities.pop || ''
97
+ @input = entities.pop || empty_string
100
98
  end
101
99
 
102
100
  check_buffer_limits
@@ -111,7 +109,7 @@ module UV
111
109
  messages = @input.split(@indicator, -1)
112
110
  messages.shift # discard junk data
113
111
 
114
- last = messages.pop || ''
112
+ last = messages.pop || empty_string
115
113
 
116
114
  # Select messages of the right size then remove junk data
117
115
  messages.select! { |msg| msg.length >= @msg_length ? true : false }
@@ -146,15 +144,27 @@ module UV
146
144
  end
147
145
 
148
146
  def init_buffer
149
- @input = ''
150
- @input.force_encoding(@encoding)
151
- @delimiter.force_encoding(@encoding) if @delimiter.is_a?(String)
152
- @indicator.force_encoding(@encoding) if @indicator.is_a?(String)
147
+ @input = empty_string
148
+
149
+ if @delimiter.is_a?(String)
150
+ @delimiter = String.new(@delimiter).encode(@encoding).freeze
151
+ end
152
+
153
+ if @indicator.is_a?(String)
154
+ @indicator = String.new(@indicator).encode(@encoding).freeze
155
+ end
153
156
  end
154
157
 
155
- def reset(value = '')
156
- @input = value
157
- @input.force_encoding(@encoding)
158
+ def reset(value = nil)
159
+ @input = String.new(value || '').force_encoding(@encoding)
160
+ end
161
+
162
+
163
+ protected
164
+
165
+
166
+ def empty_string
167
+ String.new.force_encoding(@encoding)
158
168
  end
159
169
  end
160
170
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module UV
3
4
  def self.try_connect(tcp, handler, server, port)
@@ -13,12 +14,12 @@ module UV
13
14
  tcp.start_read
14
15
  end
15
16
  else
16
- tcp.loop.lookup(server).then(
17
+ tcp.reactor.lookup(server, wait: false).then(
17
18
  proc { |result|
18
19
  UV.try_connect(tcp, handler, result[0][0], port)
19
20
  },
20
21
  proc { |failure|
21
- # TODO:: Log error on loop
22
+ # TODO:: Log error on reactor
22
23
  handler.on_close
23
24
  }
24
25
  )
@@ -64,7 +65,7 @@ module UV
64
65
 
65
66
  class TcpConnection < Connection
66
67
  def write(data)
67
- @transport.write(data)
68
+ @transport.write(data, wait: :promise)
68
69
  end
69
70
 
70
71
  def close_connection(after_writing = false)
@@ -76,9 +77,8 @@ module UV
76
77
  end
77
78
 
78
79
  def stream_file(filename, type = :raw)
79
- file = @loop.file(filename, File::RDONLY)
80
- file.progress do # File is open and available for reading
81
- file.send_file(@transport, type).finally do
80
+ file = @reactor.file(filename, File::RDONLY) do # File is open and available for reading
81
+ file.send_file(@transport, type, wait: :promise).finally do
82
82
  file.close
83
83
  end
84
84
  end
@@ -105,7 +105,7 @@ module UV
105
105
  def initialize(tcp)
106
106
  super()
107
107
 
108
- @loop = tcp.loop
108
+ @reactor = tcp.reactor
109
109
  @transport = tcp
110
110
  @transport.finally method(:on_close)
111
111
  @transport.progress method(:on_read)
@@ -127,10 +127,10 @@ module UV
127
127
  def initialize(server, port)
128
128
  super()
129
129
 
130
- @loop = Libuv::Loop.current || Libuv::Loop.default
130
+ @reactor = reactor
131
131
  @server = server
132
132
  @port = port
133
- @transport = @loop.tcp
133
+ @transport = @reactor.tcp
134
134
 
135
135
  ::UV.try_connect(@transport, self, @server, @port)
136
136
  end
@@ -146,9 +146,9 @@ module UV
146
146
  end
147
147
 
148
148
  def reconnect(server = nil, port = nil)
149
- @loop = Libuv::Loop.current || @loop
149
+ @reactor = reactor
150
150
 
151
- @transport = @loop.tcp
151
+ @transport = @reactor.tcp
152
152
  @server = server || @server
153
153
  @port = port || @port
154
154
 
@@ -160,8 +160,8 @@ module UV
160
160
  def initialize(server = nil, port = nil)
161
161
  super()
162
162
 
163
- @loop = Libuv::Loop.current || Libuv::Loop.default
164
- @transport = @loop.udp
163
+ @reactor = reactor
164
+ @transport = @reactor.udp
165
165
  @transport.progress method(:on_read)
166
166
 
167
167
  if not server.nil?
@@ -182,7 +182,7 @@ module UV
182
182
  else
183
183
  # Async DNS resolution
184
184
  # Note:: send here will chain the promise
185
- @loop.lookup(recipient_address).then do |result|
185
+ @reactor.lookup(recipient_address).then do |result|
186
186
  @transport.send result[0][0], recipient_port, data
187
187
  end
188
188
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
2
+
1
3
  module UV
2
4
  module Http
3
5
  module Encoding
@@ -105,7 +107,7 @@ module UV
105
107
  end
106
108
 
107
109
  def encode_headers(head)
108
- head.inject('') do |result, (key, value)|
110
+ head.inject(String.new) do |result, (key, value)|
109
111
  # Munge keys from foo-bar-baz to Foo-Bar-Baz
110
112
  key = key.split('-').map { |k| k.to_s.capitalize }.join('-')
111
113
  result << case key
@@ -119,7 +121,7 @@ module UV
119
121
 
120
122
  def encode_cookie(cookie)
121
123
  if cookie.is_a? Hash
122
- cookie.inject('') { |result, (k, v)| result << encode_param(k, v) + ';' }
124
+ cookie.inject(String.new) { |result, (k, v)| result << encode_param(k, v) + ';' }
123
125
  else
124
126
  cookie
125
127
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module UV
2
4
  module Http
3
5
  class Headers < Hash
@@ -7,6 +9,9 @@ module UV
7
9
  # The status code (as an integer)
8
10
  attr_accessor :status
9
11
 
12
+ # The text after the status code
13
+ attr_accessor :reason_phrase
14
+
10
15
  # Cookies at the time of the request
11
16
  attr_accessor :cookies
12
17
 
@@ -57,12 +62,14 @@ module UV
57
62
  # Parser Callbacks:
58
63
  def on_message_begin(parser)
59
64
  @headers = Headers.new
60
- @body = ''
65
+ @body = String.new
61
66
  @chunked = false
62
67
  @close_connection = false
63
68
  end
64
69
 
65
70
  def on_status(parser, data)
71
+ @headers.reason_phrase = data
72
+
66
73
  # Different HTTP versions have different defaults
67
74
  if @state.http_minor == 0
68
75
  @close_connection = true
@@ -88,6 +95,11 @@ module UV
88
95
  # If chunked we'll buffer streaming data for notification
89
96
  @chunked = data == 'chunked'
90
97
 
98
+ end
99
+
100
+ current = @headers[@header]
101
+ if current
102
+ @headers[@header] = "#{current}, #{data}"
91
103
  else
92
104
  @headers[@header] = data
93
105
  end
@@ -134,7 +146,7 @@ module UV
134
146
  def eof
135
147
  return if @request.nil?
136
148
 
137
- if @headers_complete && @headers[:'Content-Length'].nil?
149
+ if @headers_complete && (@headers[:'Content-Length'].nil? || @request.method == :head)
138
150
  on_message_complete(nil)
139
151
  else
140
152
  # Reject if this is a partial response