dalli 2.7.2 → 2.7.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dalli might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e3197932a3392dbaf7e6c59789506a28a61e3ace
4
- data.tar.gz: f4d9b560f4294773b1555beee774c94def079f81
3
+ metadata.gz: d251aba11eb6809a22a244864c5d8d509347dac9
4
+ data.tar.gz: 5d3cb28222688e09fc202179a7bc98b2546268d6
5
5
  SHA512:
6
- metadata.gz: 07c253819a5a2f40002b48f44439bc4eaa784cf0154cf769d852b28ec26657e24696bc97832fe47f6c34ffcb81a4dbdeb643f53bca491085d298053d77c3098e
7
- data.tar.gz: 5da4300af76b9899673044ee73fbd011cfe5ce2962028692f9e47a983a0bceaeee60493a91464be44f3b05f28409f163d5d9801aa73f81e395e3f041ae61e3fd
6
+ metadata.gz: 6741b307e4311f199c8bcbaf8981838f5e3a82f920e37d138663f35fc96900b5f5f040be67358381c3b4b90893cb7e30e8871b0a6bd5fe0c10d604741d0e5a1e
7
+ data.tar.gz: 40f41b7bfb6d9de9e99cf4fcafc06112c3776c297bfe52f9525ef45209ff92e3aa52ee8831de66ea4e07f2ab3165481dae8c8d03175a0e3bda78453df4145845
data/History.md CHANGED
@@ -1,6 +1,20 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
+ 2.7.3
5
+ ==========
6
+
7
+ - Assorted spec improvements
8
+ - README changes to specify defaults for failover and compress options (keen99, #470)
9
+ - SASL authentication changes to deal with Unicode characters (flypiggy, #477)
10
+ - Call to_i on ttl to accomodate ActiveSupport::Duration (#494)
11
+ - Change to implicit blocks for performance (glaucocustodio, #495)
12
+ - Change to each_key for performance (jastix, #496)
13
+ - Support stats settings - (dterei, #500)
14
+ - Raise DallError if hostname canno be parsed (dannyfallon, #501)
15
+ - Fix instrumentation for falsey values (AlexRiedler, #514)
16
+ - Support UNIX socket configurations (r-stu31, #515)
17
+
4
18
  2.7.2
5
19
  ==========
6
20
 
data/README.md CHANGED
@@ -44,7 +44,7 @@ If you have problems, please enter an issue.
44
44
  Installation and Usage
45
45
  ------------------------
46
46
 
47
- Remember, Dalli **requires** memcached 1.4+. You can check the version with `memcached -h`. Please note that memcached that Mac OS X Snow Leopard ships with is 1.2.8 and won't work. Install 1.4.x using Homebrew with
47
+ Remember, Dalli **requires** memcached 1.4+. You can check the version with `memcached -h`. Please note that the memcached version that *Mac OS X Snow Leopard* ships with is 1.2.8 and it won't work. Install memcached 1.4.x using Homebrew with
48
48
 
49
49
  brew install memcached
50
50
 
@@ -130,7 +130,7 @@ add :pool\_size to your `dalli_store` config:
130
130
  config.cache_store = :dalli_store, 'cache-1.example.com', { :pool_size => 5 }
131
131
  ```
132
132
 
133
- You can then use the Rails cache as normal or check out a Dalli client directly from the pool:
133
+ You can then use the Rails cache as normal and Rails.cache will use the pool transparently under the covers, or you can check out a Dalli client directly from the pool:
134
134
 
135
135
  ```ruby
136
136
  Rails.cache.fetch('foo', :expires_in => 300) do
@@ -151,16 +151,26 @@ Dalli::Client accepts the following options. All times are in seconds.
151
151
 
152
152
  **expires_in**: Global default for key TTL. Default is 0, which means no expiry.
153
153
 
154
- **failover**: Boolean, if true Dalli will failover to another server if the main server for a key is down.
154
+ **namespace**: If specified, prepends each key with this value to provide simple namespacing. Default is nil.
155
155
 
156
- **compress**: Boolean, if true Dalli will gzip-compress values larger than 1K.
156
+ **failover**: Boolean, if true Dalli will failover to another server if the main server for a key is down. Default is true.
157
+
158
+ **threadsafe**: Boolean. If true Dalli ensures that only one thread is using a socket at a given time. Default is true. Set to false at your own peril.
159
+
160
+ **serializer**: The serializer to use for objects being stored (ex. JSON).
161
+ Default is Marshal.
162
+
163
+ **compress**: Boolean, if true Dalli will gzip-compress values larger than 1K. Default is false.
157
164
 
158
165
  **compression_min_size**: Minimum value byte size for which to attempt compression. Default is 1K.
159
166
 
160
167
  **compression_max_size**: Maximum value byte size for which to attempt compression. Default is unlimited.
161
168
 
162
- **serializer**: The serializer to use for objects being stored (ex. JSON).
163
- Default is Marshal.
169
+ **compressor**: The compressor to use for objects being stored.
170
+ Default is zlib, implemented under `Dalli::Compressor`.
171
+ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzip_flag 2` and use `Dalli::GzipCompressor`
172
+
173
+ **keepalive**: Boolean. If true, Dalli will enable keep-alive for socket connections. Default is true.
164
174
 
165
175
  **socket_timeout**: Timeout for all socket operations (connect, read, write). Default is 0.5.
166
176
 
@@ -168,7 +178,7 @@ Default is Marshal.
168
178
 
169
179
  **socket_failure_delay**: Before retrying a socket operation, the process sleeps for this amount of time. Default is 0.01. Set to nil for no delay.
170
180
 
171
- **down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value to low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is 1 second.
181
+ **down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value too low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is 1 second.
172
182
 
173
183
  **value_max_bytes**: The maximum size of a value in memcached. Defaults to 1MB, this can be increased with memcached's -I parameter. You must also configure Dalli to allow the larger size here.
174
184
 
@@ -176,12 +186,6 @@ Default is Marshal.
176
186
 
177
187
  **password**: The password to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
178
188
 
179
- **keepalive**: Boolean. If true, Dalli will enable keep-alive for socket connections. Default is true.
180
-
181
- **compressor**: The compressor to use for objects being stored.
182
- Default is zlib, implemented under `Dalli::Compressor`.
183
- If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzip_flag 2` and use `Dalli::GzipCompressor`
184
-
185
189
  Features and Changes
186
190
  ------------------------
187
191
 
@@ -205,7 +209,7 @@ We're not accepting new compressors. They are trivial to add in an initializer.
205
209
  Thanks
206
210
  ------------
207
211
 
208
- Eric Wong - for help using his [kgio](http://unicorn.bogomips.org/kgio/index.html) library.
212
+ Eric Wong - for help using his [kgio](http://bogomips.org/kgio/) library.
209
213
 
210
214
  Brian Mitchell - for his remix-stash project which was helpful when implementing and testing the binary protocol support.
211
215
 
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ require 'bundler/gem_tasks'
1
2
  require 'appraisal'
2
3
  require 'rake/testtask'
3
4
  Rake::TestTask.new(:test) do |test|
@@ -83,15 +83,15 @@ module ActiveSupport
83
83
 
84
84
  def fetch(name, options=nil)
85
85
  options ||= {}
86
- name = expanded_key name
86
+ namespaced_name = namespaced_key(name, options)
87
87
 
88
88
  if block_given?
89
89
  unless options[:force]
90
- entry = instrument(:read, name, options) do |payload|
91
- read_entry(name, options).tap do |result|
90
+ entry = instrument(:read, namespaced_name, options) do |payload|
91
+ read_entry(namespaced_name, options).tap do |result|
92
92
  if payload
93
93
  payload[:super_operation] = :fetch
94
- payload[:hit] = !!result
94
+ payload[:hit] = !result.nil?
95
95
  end
96
96
  end
97
97
  end
@@ -114,18 +114,18 @@ module ActiveSupport
114
114
 
115
115
  def read(name, options=nil)
116
116
  options ||= {}
117
- name = expanded_key name
117
+ name = namespaced_key(name, options)
118
118
 
119
119
  instrument(:read, name, options) do |payload|
120
120
  entry = read_entry(name, options)
121
- payload[:hit] = !!entry if payload
121
+ payload[:hit] = !entry.nil? if payload
122
122
  entry
123
123
  end
124
124
  end
125
125
 
126
126
  def write(name, value, options=nil)
127
127
  options ||= {}
128
- name = expanded_key name
128
+ name = namespaced_key(name, options)
129
129
 
130
130
  instrument(:write, name, options) do |payload|
131
131
  with do |connection|
@@ -137,7 +137,7 @@ module ActiveSupport
137
137
 
138
138
  def exist?(name, options=nil)
139
139
  options ||= {}
140
- name = expanded_key name
140
+ name = namespaced_key(name, options)
141
141
 
142
142
  log(:exist, name, options)
143
143
  !read_entry(name, options).nil?
@@ -145,7 +145,7 @@ module ActiveSupport
145
145
 
146
146
  def delete(name, options=nil)
147
147
  options ||= {}
148
- name = expanded_key name
148
+ name = namespaced_key(name, options)
149
149
 
150
150
  instrument(:delete, name, options) do |payload|
151
151
  delete_entry(name, options)
@@ -155,12 +155,12 @@ module ActiveSupport
155
155
  # Reads multiple keys from the cache using a single call to the
156
156
  # servers for all keys. Keys must be Strings.
157
157
  def read_multi(*names)
158
- names.extract_options!
159
- mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
158
+ options = names.extract_options!
159
+ mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
160
160
  instrument(:read_multi, names) do
161
161
  results = {}
162
162
  if local_cache
163
- mapping.keys.each do |key|
163
+ mapping.each_key do |key|
164
164
  if value = local_cache.read_entry(key, options)
165
165
  results[key] = value
166
166
  end
@@ -186,7 +186,7 @@ module ActiveSupport
186
186
  # and the result will be written to the cache and returned.
187
187
  def fetch_multi(*names)
188
188
  options = names.extract_options!
189
- mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
189
+ mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
190
190
 
191
191
  instrument(:fetch_multi, names) do
192
192
  with do |connection|
@@ -216,7 +216,7 @@ module ActiveSupport
216
216
  # memcached counters cannot hold negative values.
217
217
  def increment(name, amount = 1, options=nil)
218
218
  options ||= {}
219
- name = expanded_key name
219
+ name = namespaced_key(name, options)
220
220
  initial = options.has_key?(:initial) ? options[:initial] : amount
221
221
  expires_in = options[:expires_in]
222
222
  instrument(:increment, name, :amount => amount) do
@@ -235,7 +235,7 @@ module ActiveSupport
235
235
  # memcached counters cannot hold negative values.
236
236
  def decrement(name, amount = 1, options=nil)
237
237
  options ||= {}
238
- name = expanded_key name
238
+ name = namespaced_key(name, options)
239
239
  initial = options.has_key?(:initial) ? options[:initial] : 0
240
240
  expires_in = options[:expires_in]
241
241
  instrument(:decrement, name, :amount => amount) do
@@ -317,6 +317,15 @@ module ActiveSupport
317
317
  end
318
318
 
319
319
  private
320
+
321
+ def namespaced_key(key, options)
322
+ key = expanded_key(key)
323
+ namespace = options[:namespace] if options
324
+ prefix = namespace.is_a?(Proc) ? namespace.call : namespace
325
+ key = "#{prefix}:#{key}" if prefix
326
+ key
327
+ end
328
+
320
329
  # Expand key to be a consistent string value. Invoke +cache_key+ if
321
330
  # object responds to +cache_key+. Otherwise, to_param method will be
322
331
  # called. If the key is a Hash, then keys will be sorted alphabetically.
@@ -9,12 +9,15 @@ module Dalli
9
9
  # Dalli::Client is the main class which developers will use to interact with
10
10
  # the memcached server. Usage:
11
11
  #
12
- # Dalli::Client.new(['localhost:11211:10', 'cache-2.example.com:11211:5', '192.168.0.1:22122:5'],
12
+ # Dalli::Client.new(['localhost:11211:10', 'cache-2.example.com:11211:5', '192.168.0.1:22122:5', '/var/run/memcached/socket'],
13
13
  # :threadsafe => true, :failover => true, :expires_in => 300)
14
14
  #
15
15
  # servers is an Array of "host:port:weight" where weight allows you to distribute cache unevenly.
16
16
  # Both weight and port are optional. If you pass in nil, Dalli will use the <tt>MEMCACHE_SERVERS</tt>
17
- # environment variable or default to 'localhost:11211' if it is not present.
17
+ # environment variable or default to 'localhost:11211' if it is not present. Dalli also supports
18
+ # the ability to connect to Memcached on localhost through a UNIX socket. To use this functionality,
19
+ # use a full pathname (beginning with a slash character '/') in place of the "host:port" pair in
20
+ # the server configuration.
18
21
  #
19
22
  # Options:
20
23
  # - :namespace - prepend each key with this value to provide simple namespacing.
@@ -58,10 +61,11 @@ module Dalli
58
61
  # If a block is given, yields key/value pairs one at a time.
59
62
  # Otherwise returns a hash of { 'key' => 'value', 'key2' => 'value1' }
60
63
  def get_multi(*keys)
64
+ return {} if keys.flatten.compact.empty?
61
65
  if block_given?
62
66
  get_multi_yielder(keys) {|k, data| yield k, data.first}
63
67
  else
64
- Hash.new.tap do |hash|
68
+ Hash.new.tap do |hash|
65
69
  get_multi_yielder(keys) {|k, data| hash[k] = data.first}
66
70
  end
67
71
  end
@@ -88,12 +92,12 @@ module Dalli
88
92
  # - nil if the key did not exist.
89
93
  # - false if the value was changed by someone else.
90
94
  # - true if the value was successfully updated.
91
- def cas(key, ttl=nil, options=nil, &block)
95
+ def cas(key, ttl=nil, options=nil)
92
96
  ttl ||= @options[:expires_in].to_i
93
97
  (value, cas) = perform(:cas, key)
94
98
  value = (!value || value == 'Not found') ? nil : value
95
99
  if value
96
- newvalue = block.call(value)
100
+ newvalue = yield(value)
97
101
  perform(:set, key, newvalue, ttl, cas, options)
98
102
  end
99
103
  end
@@ -193,13 +197,13 @@ module Dalli
193
197
 
194
198
  ##
195
199
  # Collect the stats for each server.
196
- # You can optionally pass a type including :items or :slabs to get specific stats
200
+ # You can optionally pass a type including :items, :slabs or :settings to get specific stats
197
201
  # Returns a hash like { 'hostname:port' => { 'stat1' => 'value1', ... }, 'hostname2:port' => { ... } }
198
202
  def stats(type=nil)
199
- type = nil if ![nil, :items,:slabs].include? type
203
+ type = nil if ![nil, :items,:slabs,:settings].include? type
200
204
  values = {}
201
205
  ring.servers.each do |server|
202
- values["#{server.hostname}:#{server.port}"] = server.alive? ? server.request(:stats,type.to_s) : nil
206
+ values["#{server.name}"] = server.alive? ? server.request(:stats,type.to_s) : nil
203
207
  end
204
208
  values
205
209
  end
@@ -223,7 +227,7 @@ module Dalli
223
227
  def version
224
228
  values = {}
225
229
  ring.servers.each do |server|
226
- values["#{server.hostname}:#{server.port}"] = server.alive? ? server.request(:version) : nil
230
+ values["#{server.name}"] = server.alive? ? server.request(:version) : nil
227
231
  end
228
232
  values
229
233
  end
@@ -272,7 +276,7 @@ module Dalli
272
276
  server.request(:send_multiget, keys_for_server)
273
277
  rescue DalliError, NetworkError => e
274
278
  Dalli.logger.debug { e.inspect }
275
- Dalli.logger.debug { "unable to get keys for server #{server.hostname}:#{server.port}" }
279
+ Dalli.logger.debug { "unable to get keys for server #{server.name}" }
276
280
  end
277
281
  end
278
282
  end
@@ -15,7 +15,7 @@ module Dalli
15
15
  continuum = []
16
16
  servers.each do |server|
17
17
  entry_count_for(server, servers.size, total_weight).times do |idx|
18
- hash = Digest::SHA1.hexdigest("#{server.hostname}:#{server.port}:#{idx}")
18
+ hash = Digest::SHA1.hexdigest("#{server.name}:#{idx}")
19
19
  value = Integer("0x#{hash[0..7]}")
20
20
  continuum << Dalli::Ring::Entry.new(value, server)
21
21
  end
@@ -8,7 +8,10 @@ module Dalli
8
8
  attr_accessor :weight
9
9
  attr_accessor :options
10
10
  attr_reader :sock
11
+ attr_reader :socket_type # possible values: :unix, :tcp
11
12
 
13
+ DEFAULT_PORT = 11211
14
+ DEFAULT_WEIGHT = 1
12
15
  DEFAULTS = {
13
16
  # seconds between trying to contact a remote server
14
17
  :down_retry_delay => 1,
@@ -32,11 +35,7 @@ module Dalli
32
35
  }
33
36
 
34
37
  def initialize(attribs, options = {})
35
- (@hostname, @port, @weight) = parse_hostname(attribs)
36
- @port ||= 11211
37
- @port = Integer(@port)
38
- @weight ||= 1
39
- @weight = Integer(@weight)
38
+ @hostname, @port, @weight, @socket_type = parse_hostname(attribs)
40
39
  @fail_count = 0
41
40
  @down_at = nil
42
41
  @last_down_at = nil
@@ -49,13 +48,17 @@ module Dalli
49
48
  end
50
49
 
51
50
  def name
52
- "#{@hostname}:#{@port}"
51
+ if socket_type == :unix
52
+ hostname
53
+ else
54
+ "#{hostname}:#{port}"
55
+ end
53
56
  end
54
57
 
55
58
  # Chokepoint method for instrumentation
56
59
  def request(op, *args)
57
60
  verify_state
58
- raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." unless alive?
61
+ raise Dalli::NetworkError, "#{name} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." unless alive?
59
62
  begin
60
63
  send(op, *args)
61
64
  rescue Dalli::NetworkError
@@ -67,9 +70,10 @@ module Dalli
67
70
  false
68
71
  rescue Dalli::DalliError
69
72
  raise
73
+ rescue Timeout::Error
74
+ raise
70
75
  rescue => ex
71
- Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
72
- Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
76
+ Dalli.logger.error "Unexpected exception during Dalli request: #{ex.class.name}: #{ex.message}"
73
77
  Dalli.logger.error ex.backtrace.join("\n\t")
74
78
  down!
75
79
  end
@@ -80,7 +84,7 @@ module Dalli
80
84
 
81
85
  if @last_down_at && @last_down_at + options[:down_retry_delay] >= Time.now
82
86
  time = @last_down_at + options[:down_retry_delay] - Time.now
83
- Dalli.logger.debug { "down_retry_delay not reached for #{hostname}:#{port} (%.3f seconds left)" % time }
87
+ Dalli.logger.debug { "down_retry_delay not reached for #{name} (%.3f seconds left)" % time }
84
88
  return false
85
89
  end
86
90
 
@@ -203,7 +207,7 @@ module Dalli
203
207
  end
204
208
 
205
209
  def failure!(exception)
206
- message = "#{hostname}:#{port} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
210
+ message = "#{name} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
207
211
  Dalli.logger.info { message }
208
212
 
209
213
  @fail_count += 1
@@ -223,21 +227,21 @@ module Dalli
223
227
 
224
228
  if @down_at
225
229
  time = Time.now - @down_at
226
- Dalli.logger.debug { "#{hostname}:#{port} is still down (for %.3f seconds now)" % time }
230
+ Dalli.logger.debug { "#{name} is still down (for %.3f seconds now)" % time }
227
231
  else
228
232
  @down_at = @last_down_at
229
- Dalli.logger.warn { "#{hostname}:#{port} is down" }
233
+ Dalli.logger.warn { "#{name} is down" }
230
234
  end
231
235
 
232
236
  @error = $! && $!.class.name
233
237
  @msg = @msg || ($! && $!.message && !$!.message.empty? && $!.message)
234
- raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}"
238
+ raise Dalli::NetworkError, "#{name} is down: #{@error} #{@msg}"
235
239
  end
236
240
 
237
241
  def up!
238
242
  if @down_at
239
243
  time = Time.now - @down_at
240
- Dalli.logger.warn { "#{hostname}:#{port} is back (downtime was %.3f seconds)" % time }
244
+ Dalli.logger.warn { "#{name} is back (downtime was %.3f seconds)" % time }
241
245
  end
242
246
 
243
247
  @fail_count = 0
@@ -396,6 +400,8 @@ module Dalli
396
400
  marshalled = true
397
401
  begin
398
402
  self.serializer.dump(value)
403
+ rescue TimeoutError => e
404
+ raise e
399
405
  rescue => ex
400
406
  # Marshalling can throw several different types of generic Ruby exceptions.
401
407
  # Convert to a specific exception so we can special case it higher up the stack.
@@ -467,9 +473,9 @@ module Dalli
467
473
  def sanitize_ttl(ttl)
468
474
  if ttl > MAX_ACCEPTABLE_EXPIRATION_INTERVAL
469
475
  Dalli.logger.debug "Expiration interval too long for Memcached, converting to an expiration timestamp"
470
- Time.now.to_i + ttl
476
+ Time.now.to_i + ttl.to_i
471
477
  else
472
- ttl
478
+ ttl.to_i
473
479
  end
474
480
  end
475
481
 
@@ -555,13 +561,17 @@ module Dalli
555
561
  end
556
562
 
557
563
  def connect
558
- Dalli.logger.debug { "Dalli::Server#connect #{hostname}:#{port}" }
564
+ Dalli.logger.debug { "Dalli::Server#connect #{name}" }
559
565
 
560
566
  begin
561
567
  @pid = Process.pid
562
- @sock = KSocket.open(hostname, port, self, options)
563
- @version = version # trigger actual connect
568
+ if socket_type == :unix
569
+ @sock = KSocket::UNIX.open(hostname, self, options)
570
+ else
571
+ @sock = KSocket::TCP.open(hostname, port, self, options)
572
+ end
564
573
  sasl_authentication if need_auth?
574
+ @version = version # trigger actual connect
565
575
  up!
566
576
  rescue Dalli::DalliError # SASL auth failure
567
577
  raise
@@ -666,7 +676,7 @@ module Dalli
666
676
 
667
677
  (extras, type, status, count) = read_header.unpack(NORMAL_HEADER)
668
678
  raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
669
- content = read(count)
679
+ content = read(count).gsub(/\u0000/, ' ')
670
680
  return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
671
681
  mechanisms = content.split(' ')
672
682
  raise NotImplementedError, "Dalli only supports the PLAIN authentication mechanism" if !mechanisms.include?('PLAIN')
@@ -689,8 +699,23 @@ module Dalli
689
699
  end
690
700
 
691
701
  def parse_hostname(str)
692
- res = str.match(/\A(\[([\h:]+)\]|[^:]+)(:(\d+))?(:(\d+))?\z/)
693
- return res[2] || res[1], res[4], res[6]
702
+ res = str.match(/\A(\[([\h:]+)\]|[^:]+)(?::(\d+))?(?::(\d+))?\z/)
703
+ raise Dalli::DalliError, "Could not parse hostname #{str}" if res.nil? || res[1] == '[]'
704
+ hostnam = res[2] || res[1]
705
+ if hostnam =~ /\A\//
706
+ socket_type = :unix
707
+ # in case of unix socket, allow only setting of weight, not port
708
+ raise Dalli::DalliError, "Could not parse hostname #{str}" if res[4]
709
+ weigh = res[3]
710
+ else
711
+ socket_type = :tcp
712
+ por = res[3] || DEFAULT_PORT
713
+ por = Integer(por)
714
+ weigh = res[4]
715
+ end
716
+ weigh ||= DEFAULT_WEIGHT
717
+ weigh = Integer(weigh)
718
+ return hostnam, por, weigh, socket_type
694
719
  end
695
720
  end
696
721
  end