1414# statsd = Statsd.new('localhost').tap{|sd| sd.namespace = 'account'}
1515# statsd.increment 'activate'
1616class Statsd
17- class RubyUdpClient
18- attr_reader :key , :sock
17+ class SecureUDPClient < UDPClient
18+ def initialize ( address , port , key )
19+ super ( address , port )
20+ @key = key
21+ end
22+
23+ def send ( msg )
24+ super ( signed_payload ( msg ) )
25+ end
26+
27+ private
28+ # defer loading openssl and securerandom unless needed. this shaves ~10ms off
29+ # of baseline require load time for environments that don't require message signing.
30+ def self . setup_openssl
31+ @sha256 ||= begin
32+ require 'securerandom'
33+ require 'openssl'
34+ OpenSSL ::Digest ::SHA256 . new
35+ end
36+ end
37+
38+ def signed_payload ( message )
39+ sha256 = SecureUDPClient . setup_openssl
40+ payload = timestamp + nonce + message
41+ signature = OpenSSL ::HMAC . digest ( sha256 , @key , payload )
42+ signature + payload
43+ end
44+
45+ def timestamp
46+ [ Time . now . to_i ] . pack ( "Q<" )
47+ end
48+
49+ def nonce
50+ SecureRandom . random_bytes ( 4 )
51+ end
52+ end
53+
54+ class UDPClient
55+ attr_reader :sock
1956
20- def initialize ( address , port , key = nil )
57+ def initialize ( address , port = nil )
58+ address , port = address . split ( ':' ) if address . include? ( ':' )
2159 addrinfo = Addrinfo . ip ( address )
60+
2261 @sock = UDPSocket . new ( addrinfo . pfamily )
2362 @sock . connect ( addrinfo . ip_address , port )
24- @key = key
2563 end
2664
2765 def send ( msg )
@@ -52,17 +90,16 @@ def namespace=(namespace)
5290
5391 def initialize ( client_class = nil )
5492 @shards = [ ]
55- @client_class = client_class || RubyUdpClient
93+ @client_class = client_class || UDPClient
5694 self . namespace = nil
5795 end
5896
5997 def self . simple ( addr , port = nil )
6098 self . new . add_shard ( addr , port )
6199 end
62100
63- def add_shard ( addr , port = nil , key = nil )
64- addr , port = addr . split ( ':' ) if addr . include? ( ':' )
65- @shards << @client_class . new ( addr , port . to_i , key )
101+ def add_shard ( *args )
102+ @shards << @client_class . new ( *args )
66103 self
67104 end
68105
@@ -129,7 +166,6 @@ def time(stat, sample_rate=1)
129166 def histogram ( stat , value , sample_rate = 1 ) ; send stat , value , HISTOGRAM_TYPE , sample_rate end
130167
131168 private
132-
133169 def sampled ( sample_rate )
134170 yield unless sample_rate < 1 and rand > sample_rate
135171 end
@@ -140,7 +176,7 @@ def send(stat, delta, type, sample_rate=1)
140176 stat . gsub! ( /::/ , "." . freeze )
141177 stat . gsub! ( RESERVED_CHARS_REGEX , "_" . freeze )
142178
143- msg = ""
179+ msg = String . new
144180 msg << @prefix
145181 msg << stat
146182 msg << ":" . freeze
@@ -153,7 +189,7 @@ def send(stat, delta, type, sample_rate=1)
153189 end
154190
155191 shard = select_shard ( stat )
156- shard . send ( shard . key ? signed_payload ( shard . key , msg ) : msg )
192+ shard . send ( msg )
157193 end
158194 end
159195
@@ -164,29 +200,4 @@ def select_shard(stat)
164200 @shards [ Zlib . crc32 ( stat ) % @shards . size ]
165201 end
166202 end
167-
168- def signed_payload ( key , message )
169- sha256 = Statsd . setup_openssl
170- payload = timestamp + nonce + message
171- signature = OpenSSL ::HMAC . digest ( sha256 , key , payload )
172- signature + payload
173- end
174-
175- # defer loading openssl and securerandom unless needed. this shaves ~10ms off
176- # of baseline require load time for environments that don't require message signing.
177- def self . setup_openssl
178- @sha256 ||= begin
179- require 'securerandom'
180- require 'openssl'
181- OpenSSL ::Digest ::SHA256 . new
182- end
183- end
184-
185- def timestamp
186- [ Time . now . to_i ] . pack ( "Q<" )
187- end
188-
189- def nonce
190- SecureRandom . random_bytes ( 4 )
191- end
192203end
0 commit comments