gem 0.0.1.alpha

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.
@@ -0,0 +1,240 @@
1
+ # -*- coding: utf-8 -*-
2
+ #--
3
+ # Copyright (C) 2004 Mauricio Julio Fernández Pradier
4
+ # See LICENSE.txt for additional licensing information.
5
+ #++
6
+
7
+ ##
8
+ # Allows writing of tar files
9
+
10
+ class Gem::Tar::Writer
11
+
12
+ class FileOverflow < StandardError; end
13
+
14
+ ##
15
+ # IO wrapper that allows writing a limited amount of data
16
+
17
+ class BoundedStream
18
+
19
+ ##
20
+ # Maximum number of bytes that can be written
21
+
22
+ attr_reader :limit
23
+
24
+ ##
25
+ # Number of bytes written
26
+
27
+ attr_reader :written
28
+
29
+ ##
30
+ # Wraps +io+ and allows up to +limit+ bytes to be written
31
+
32
+ def initialize(io, limit)
33
+ @io = io
34
+ @limit = limit
35
+ @written = 0
36
+ end
37
+
38
+ ##
39
+ # Writes +data+ onto the IO, raising a FileOverflow exception if the
40
+ # number of bytes will be more than #limit
41
+
42
+ def write(data)
43
+ if data.size + @written > @limit
44
+ raise FileOverflow, "You tried to feed more data than fits in the file."
45
+ end
46
+ @io.write data
47
+ @written += data.size
48
+ data.size
49
+ end
50
+
51
+ end
52
+
53
+ ##
54
+ # IO wrapper that provides only #write
55
+
56
+ class RestrictedStream
57
+
58
+ ##
59
+ # Creates a new RestrictedStream wrapping +io+
60
+
61
+ def initialize(io)
62
+ @io = io
63
+ end
64
+
65
+ ##
66
+ # Writes +data+ onto the IO
67
+
68
+ def write(data)
69
+ @io.write data
70
+ end
71
+
72
+ end
73
+
74
+ ##
75
+ # Creates a new TarWriter, yielding it if a block is given
76
+
77
+ def self.new(io)
78
+ writer = super
79
+
80
+ return writer unless block_given?
81
+
82
+ begin
83
+ yield writer
84
+ ensure
85
+ writer.close
86
+ end
87
+
88
+ nil
89
+ end
90
+
91
+ ##
92
+ # Creates a new TarWriter that will write to +io+
93
+
94
+ def initialize(io)
95
+ @io = io
96
+ @closed = false
97
+ end
98
+
99
+ ##
100
+ # Adds file +name+ with permissions +mode+, and yields an IO for writing the
101
+ # file to
102
+
103
+ def add_file(name, mode) # :yields: io
104
+ check_closed
105
+
106
+ raise NonSeekableIO unless @io.respond_to? :pos=
107
+
108
+ name, prefix = split_name name
109
+
110
+ init_pos = @io.pos
111
+ @io.write "\0" * 512 # placeholder for the header
112
+
113
+ yield RestrictedStream.new(@io) if block_given?
114
+
115
+ size = @io.pos - init_pos - 512
116
+
117
+ remainder = (512 - (size % 512)) % 512
118
+ @io.write "\0" * remainder
119
+
120
+ final_pos = @io.pos
121
+ @io.pos = init_pos
122
+
123
+ header = Header.new :name => name, :mode => mode,
124
+ :size => size, :prefix => prefix
125
+
126
+ @io.write header
127
+ @io.pos = final_pos
128
+
129
+ self
130
+ end
131
+
132
+ ##
133
+ # Add file +name+ with permissions +mode+ +size+ bytes long. Yields an IO
134
+ # to write the file to.
135
+
136
+ def add_file_simple(name, mode, size) # :yields: io
137
+ check_closed
138
+
139
+ name, prefix = split_name name
140
+
141
+ header = Header.new(:name => name, :mode => mode,
142
+ :size => size, :prefix => prefix).to_s
143
+
144
+ @io.write header
145
+ os = BoundedStream.new @io, size
146
+
147
+ yield os if block_given?
148
+
149
+ min_padding = size - os.written
150
+ @io.write("\0" * min_padding)
151
+
152
+ remainder = (512 - (size % 512)) % 512
153
+ @io.write("\0" * remainder)
154
+
155
+ self
156
+ end
157
+
158
+ ##
159
+ # Raises IOError if the TarWriter is closed
160
+
161
+ def check_closed
162
+ raise IOError, "closed #{self.class}" if closed?
163
+ end
164
+
165
+ ##
166
+ # Closes the TarWriter
167
+
168
+ def close
169
+ check_closed
170
+
171
+ @io.write "\0" * 1024
172
+ flush
173
+
174
+ @closed = true
175
+ end
176
+
177
+ ##
178
+ # Is the TarWriter closed?
179
+
180
+ def closed?
181
+ @closed
182
+ end
183
+
184
+ ##
185
+ # Flushes the TarWriter's IO
186
+
187
+ def flush
188
+ check_closed
189
+
190
+ @io.flush if @io.respond_to? :flush
191
+ end
192
+
193
+ ##
194
+ # Creates a new directory in the tar file +name+ with +mode+
195
+
196
+ def mkdir(name, mode)
197
+ check_closed
198
+
199
+ name, prefix = split_name(name)
200
+
201
+ header = Header.new :name => name, :mode => mode,
202
+ :typeflag => "5", :size => 0, :prefix => prefix
203
+
204
+ @io.write header
205
+
206
+ self
207
+ end
208
+
209
+ ##
210
+ # Splits +name+ into a name and prefix that can fit in the TarHeader
211
+
212
+ def split_name(name) # :nodoc:
213
+ raise TooLongFileName if name.size > 256
214
+
215
+ if name.size <= 100 then
216
+ prefix = ""
217
+ else
218
+ parts = name.split(/\//)
219
+ newname = parts.pop
220
+ nxt = ""
221
+
222
+ loop do
223
+ nxt = parts.pop
224
+ break if newname.size + 1 + nxt.size > 100
225
+ newname = nxt + "/" + newname
226
+ end
227
+
228
+ prefix = (parts + [nxt]).join "/"
229
+ name = newname
230
+
231
+ if name.size > 100 or prefix.size > 155 then
232
+ raise TooLongFileName
233
+ end
234
+ end
235
+
236
+ return name, prefix
237
+ end
238
+
239
+ end
240
+
@@ -0,0 +1,59 @@
1
+ require 'thread'
2
+
3
+ class ThreadPool
4
+ def initialize size=4
5
+ @size = size
6
+ @queue = SizedQueue.new 1
7
+ @queue_mutex = Mutex.new
8
+ @threads = @size.times.map { Thread.new &method(:worker) }
9
+ end
10
+
11
+ def enqueue *arguments, &work
12
+ @queue_mutex.synchronize do
13
+ @queue << [work, *arguments]
14
+ end
15
+ end
16
+
17
+ def join
18
+ @queue_mutex.synchronize do
19
+ @queue << nil
20
+ @threads.each do |thread|
21
+ thread[:mutex].synchronize do
22
+ thread.kill
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def worker
31
+ mutex = Thread.current[:mutex] = Mutex.new
32
+ loop do
33
+ if work = @queue.shift
34
+ mutex.synchronize do
35
+ work.shift.call *work
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ module ThreadPoolable
43
+ def in_thread_pool size=4, &block
44
+ size = size[:of] if size.is_a? Hash
45
+ pool = ThreadPool.new size
46
+ each do |*args|
47
+ pool.enqueue *args, &block
48
+ end
49
+ pool.join
50
+ end
51
+
52
+ def in_threads &block
53
+ map do |*args|
54
+ Thread.new *args, &block
55
+ end.each(&:join)
56
+ end
57
+ end
58
+
59
+ Enumerator.send :include, ThreadPoolable
@@ -0,0 +1,73 @@
1
+ class Gem::Version
2
+ include Comparable
3
+
4
+ attr_reader :version
5
+
6
+ def initialize version=nil
7
+ self.version = version
8
+ end
9
+
10
+ def version= value
11
+ @segments = @prelease = nil
12
+ @version = value
13
+ end
14
+
15
+ def segments
16
+ @segments ||= version.to_s.split('.')
17
+ end
18
+
19
+ def prerelease?
20
+ @prerelease ||= @version =~ /[a-zA-Z]/
21
+ end
22
+
23
+ def <=> other
24
+ return unless self.class === other
25
+ return 0 if version == other.version
26
+
27
+ lhsegments = segments
28
+ rhsegments = other.segments
29
+
30
+ lhsize = lhsegments.size
31
+ rhsize = rhsegments.size
32
+ limit = (lhsize > rhsize ? lhsize : rhsize) - 1
33
+
34
+ i = 0
35
+
36
+ while i <= limit
37
+ lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
38
+ i += 1
39
+
40
+ next if lhs == rhs
41
+ return -1 if String === lhs && Numeric === rhs
42
+ return 1 if Numeric === lhs && String === rhs
43
+
44
+ return lhs <=> rhs
45
+ end
46
+
47
+ return 0
48
+ end
49
+
50
+ def marshal_dump
51
+ [version]
52
+ end
53
+
54
+ def marshal_load args
55
+ self.version = args.first
56
+ end
57
+
58
+ def empty?
59
+ version.nil? or version.empty?
60
+ end
61
+
62
+ def to_s
63
+ version
64
+ end
65
+
66
+ def inspect
67
+ "<Gem::Version #{to_s.inspect}>"
68
+ end
69
+
70
+ def to_yaml
71
+ [:@version]
72
+ end
73
+ end
@@ -0,0 +1 @@
1
+ require 'gem/requirement'
@@ -0,0 +1,27 @@
1
+ require 'net/protocol'
2
+
3
+ ##
4
+ # Aaron Patterson's monkeypatch (accepted into 1.9.1) to fix Net::HTTP's speed
5
+ # problems.
6
+ #
7
+ # http://gist.github.com/251244
8
+
9
+ class Net::BufferedIO #:nodoc:
10
+ alias :old_rbuf_fill :rbuf_fill
11
+
12
+ def rbuf_fill
13
+ if @io.respond_to? :read_nonblock then
14
+ begin
15
+ @rbuf << @io.read_nonblock(65536)
16
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN => e
17
+ retry if IO.select [@io], nil, nil, @read_timeout
18
+ raise Timeout::Error, e.message
19
+ end
20
+ else # SSL sockets do not have read_nonblock
21
+ timeout @read_timeout do
22
+ @rbuf << @io.sysread(65536)
23
+ end
24
+ end
25
+ end
26
+ end if RUBY_VERSION < '1.9'
27
+
@@ -0,0 +1,978 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'net/http/faster'
4
+ require 'uri'
5
+ require 'cgi' # for escaping
6
+
7
+ begin
8
+ require 'net/http/pipeline'
9
+ rescue LoadError
10
+ end
11
+
12
+ ##
13
+ # Persistent connections for Net::HTTP
14
+ #
15
+ # Net::HTTP::Persistent maintains persistent connections across all the
16
+ # servers you wish to talk to. For each host:port you communicate with a
17
+ # single persistent connection is created.
18
+ #
19
+ # Multiple Net::HTTP::Persistent objects will share the same set of
20
+ # connections.
21
+ #
22
+ # For each thread you start a new connection will be created. A
23
+ # Net::HTTP::Persistent connection will not be shared across threads.
24
+ #
25
+ # You can shut down the HTTP connections when done by calling #shutdown. You
26
+ # should name your Net::HTTP::Persistent object if you intend to call this
27
+ # method.
28
+ #
29
+ # Example:
30
+ #
31
+ # require 'net/http/persistent'
32
+ #
33
+ # uri = URI 'http://example.com/awesome/web/service'
34
+ #
35
+ # http = Net::HTTP::Persistent.new 'my_app_name'
36
+ #
37
+ # # perform a GET
38
+ # response = http.request uri
39
+ #
40
+ # # create a POST
41
+ # post_uri = uri + 'create'
42
+ # post = Net::HTTP::Post.new post_uri.path
43
+ # post.set_form_data 'some' => 'cool data'
44
+ #
45
+ # # perform the POST, the URI is always required
46
+ # response http.request post_uri, post
47
+ #
48
+ # == SSL
49
+ #
50
+ # SSL connections are automatically created depending upon the scheme of the
51
+ # URI. SSL connections are automatically verified against the default
52
+ # certificate store for your computer. You can override this by changing
53
+ # verify_mode or by specifying an alternate cert_store.
54
+ #
55
+ # Here are the SSL settings, see the individual methods for documentation:
56
+ #
57
+ # #certificate :: This client's certificate
58
+ # #ca_file :: The certificate-authority
59
+ # #cert_store :: An SSL certificate store
60
+ # #private_key :: The client's SSL private key
61
+ # #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
62
+ # connection
63
+ # #ssl_version :: Which specific SSL version to use
64
+ # #verify_callback :: For server certificate verification
65
+ # #verify_mode :: How connections should be verified
66
+ #
67
+ # == Proxies
68
+ #
69
+ # A proxy can be set through #proxy= or at initialization time by providing a
70
+ # second argument to ::new. The proxy may be the URI of the proxy server or
71
+ # <code>:ENV</code> which will consult environment variables.
72
+ #
73
+ # See #proxy= and #proxy_from_env for details.
74
+ #
75
+ # == Headers
76
+ #
77
+ # Headers may be specified for use in every request. #headers are appended to
78
+ # any headers on the request. #override_headers replace existing headers on
79
+ # the request.
80
+ #
81
+ # The difference between the two can be seen in setting the User-Agent. Using
82
+ # <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby,
83
+ # MyUserAgent" while <code>http.override_headers['User-Agent'] =
84
+ # 'MyUserAgent'</code> will send "MyUserAgent".
85
+ #
86
+ # == Tuning
87
+ #
88
+ # === Segregation
89
+ #
90
+ # By providing an application name to ::new you can separate your connections
91
+ # from the connections of other applications.
92
+ #
93
+ # === Idle Timeout
94
+ #
95
+ # If a connection hasn't been used for 5 seconds it will automatically be
96
+ # reset upon the next use to avoid attempting to send to a closed connection.
97
+ # This can be adjusted through idle_timeout.
98
+ #
99
+ # Reducing this value may help avoid the "too many connection resets" error
100
+ # when sending non-idempotent requests while increasing this value will cause
101
+ # fewer round-trips.
102
+ #
103
+ # === Read Timeout
104
+ #
105
+ # The amount of time allowed between reading two chunks from the socket. Set
106
+ # through #read_timeout
107
+ #
108
+ # === Open Timeout
109
+ #
110
+ # The amount of time to wait for a connection to be opened. Set through
111
+ # #open_timeout.
112
+ #
113
+ # === Idle Timeout
114
+ #
115
+ # If a connection has not been used in this many seconds it will be reset when
116
+ # a request would use the connection. The default idle timeout is unlimited.
117
+ # If you know the server's idle timeout setting this value will eliminate
118
+ # failures from attempting non-idempotent requests on closed connections. Set
119
+ # through #idle_timeout.
120
+ #
121
+ # === Socket Options
122
+ #
123
+ # Socket options may be set on newly-created connections. See #socket_options
124
+ # for details.
125
+ #
126
+ # === Non-Idempotent Requests
127
+ #
128
+ # By default non-idempotent requests will not be retried per RFC 2616. By
129
+ # setting retry_change_requests to true requests will automatically be retried
130
+ # once.
131
+ #
132
+ # Only do this when you know that retrying a POST or other non-idempotent
133
+ # request is safe for your application and will not create duplicate
134
+ # resources.
135
+ #
136
+ # The recommended way to handle non-idempotent requests is the following:
137
+ #
138
+ # require 'net/http/persistent'
139
+ #
140
+ # uri = URI 'http://example.com/awesome/web/service'
141
+ # post_uri = uri + 'create'
142
+ #
143
+ # http = Net::HTTP::Persistent.new 'my_app_name'
144
+ #
145
+ # post = Net::HTTP::Post.new post_uri.path
146
+ # # ... fill in POST request
147
+ #
148
+ # begin
149
+ # response = http.request post_uri, post
150
+ # rescue Net::HTTP::Persistent::Error
151
+ #
152
+ # # POST failed, make a new request to verify the server did not process
153
+ # # the request
154
+ # exists_uri = uri + '...'
155
+ # response = http.get exists_uri
156
+ #
157
+ # # Retry if it failed
158
+ # retry if response.code == '404'
159
+ # end
160
+ #
161
+ # The method of determining if the resource was created or not is unique to
162
+ # the particular service you are using. Of course, you will want to add
163
+ # protection from infinite looping.
164
+
165
+ class Net::HTTP::Persistent
166
+
167
+ ##
168
+ # The beginning of Time
169
+
170
+ EPOCH = Time.at 0 # :nodoc:
171
+
172
+ ##
173
+ # The version of Net::HTTP::Persistent you are using
174
+
175
+ VERSION = '2.5.2'
176
+
177
+ ##
178
+ # Error class for errors raised by Net::HTTP::Persistent. Various
179
+ # SystemCallErrors are re-raised with a human-readable message under this
180
+ # class.
181
+
182
+ class Error < StandardError; end
183
+
184
+ ##
185
+ # This client's OpenSSL::X509::Certificate
186
+
187
+ attr_reader :certificate
188
+
189
+ ##
190
+ # An SSL certificate authority. Setting this will set verify_mode to
191
+ # VERIFY_PEER.
192
+
193
+ attr_reader :ca_file
194
+
195
+ ##
196
+ # An SSL certificate store. Setting this will override the default
197
+ # certificate store. See verify_mode for more information.
198
+
199
+ attr_reader :cert_store
200
+
201
+ ##
202
+ # Sends debug_output to this IO via Net::HTTP#set_debug_output.
203
+ #
204
+ # Never use this method in production code, it causes a serious security
205
+ # hole.
206
+
207
+ attr_accessor :debug_output
208
+
209
+ ##
210
+ # Current connection generation
211
+
212
+ attr_reader :generation # :nodoc:
213
+
214
+ ##
215
+ # Where this instance's connections live in the thread local variables
216
+
217
+ attr_reader :generation_key # :nodoc:
218
+
219
+ ##
220
+ # Headers that are added to every request using Net::HTTP#add_field
221
+
222
+ attr_reader :headers
223
+
224
+ ##
225
+ # Maps host:port to an HTTP version. This allows us to enable version
226
+ # specific features.
227
+
228
+ attr_reader :http_versions
229
+
230
+ ##
231
+ # Maximum time an unused connection can remain idle before being
232
+ # automatically closed.
233
+
234
+ attr_accessor :idle_timeout
235
+
236
+ ##
237
+ # The value sent in the Keep-Alive header. Defaults to 30. Not needed for
238
+ # HTTP/1.1 servers.
239
+ #
240
+ # This may not work correctly for HTTP/1.0 servers
241
+ #
242
+ # This method may be removed in a future version as RFC 2616 does not
243
+ # require this header.
244
+
245
+ attr_accessor :keep_alive
246
+
247
+ ##
248
+ # A name for this connection. Allows you to keep your connections apart
249
+ # from everybody else's.
250
+
251
+ attr_reader :name
252
+
253
+ ##
254
+ # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout
255
+
256
+ attr_accessor :open_timeout
257
+
258
+ ##
259
+ # Headers that are added to every request using Net::HTTP#[]=
260
+
261
+ attr_reader :override_headers
262
+
263
+ ##
264
+ # This client's SSL private key
265
+
266
+ attr_reader :private_key
267
+
268
+ ##
269
+ # The URL through which requests will be proxied
270
+
271
+ attr_reader :proxy_uri
272
+
273
+ ##
274
+ # Seconds to wait until reading one block. See Net::HTTP#read_timeout
275
+
276
+ attr_accessor :read_timeout
277
+
278
+ ##
279
+ # Where this instance's request counts live in the thread local variables
280
+
281
+ attr_reader :request_key # :nodoc:
282
+
283
+ ##
284
+ # By default SSL sessions are reused to avoid extra SSL handshakes. Set
285
+ # this to false if you have problems communicating with an HTTPS server
286
+ # like:
287
+ #
288
+ # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
289
+
290
+ attr_accessor :reuse_ssl_sessions
291
+
292
+ ##
293
+ # An array of options for Socket#setsockopt.
294
+ #
295
+ # By default the TCP_NODELAY option is set on sockets.
296
+ #
297
+ # To set additional options append them to this array:
298
+ #
299
+ # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
300
+
301
+ attr_reader :socket_options
302
+
303
+ ##
304
+ # Current SSL connection generation
305
+
306
+ attr_reader :ssl_generation # :nodoc:
307
+
308
+ ##
309
+ # Where this instance's SSL connections live in the thread local variables
310
+
311
+ attr_reader :ssl_generation_key # :nodoc:
312
+
313
+ ##
314
+ # SSL version to use.
315
+ #
316
+ # By default, the version will be negotiated automatically between client
317
+ # and server. Ruby 1.9 and newer only.
318
+
319
+ attr_reader :ssl_version if RUBY_VERSION > '1.9'
320
+
321
+ ##
322
+ # Where this instance's last-use times live in the thread local variables
323
+
324
+ attr_reader :timeout_key # :nodoc:
325
+
326
+ ##
327
+ # SSL verification callback. Used when ca_file is set.
328
+
329
+ attr_reader :verify_callback
330
+
331
+ ##
332
+ # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies
333
+ # the server certificate.
334
+ #
335
+ # If no ca_file or cert_store is set the default system certificate store is
336
+ # used.
337
+ #
338
+ # You can use +verify_mode+ to override any default values.
339
+
340
+ attr_reader :verify_mode
341
+
342
+ ##
343
+ # Enable retries of non-idempotent requests that change data (e.g. POST
344
+ # requests) when the server has disconnected.
345
+ #
346
+ # This will in the worst case lead to multiple requests with the same data,
347
+ # but it may be useful for some applications. Take care when enabling
348
+ # this option to ensure it is safe to POST or perform other non-idempotent
349
+ # requests to the server.
350
+
351
+ attr_accessor :retry_change_requests
352
+
353
+ ##
354
+ # Creates a new Net::HTTP::Persistent.
355
+ #
356
+ # Set +name+ to keep your connections apart from everybody else's. Not
357
+ # required currently, but highly recommended. Your library name should be
358
+ # good enough. This parameter will be required in a future version.
359
+ #
360
+ # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from
361
+ # the environment. See proxy_from_env for details.
362
+ #
363
+ # In order to use a URI for the proxy you may need to do some extra work
364
+ # beyond URI parsing if the proxy requires a password:
365
+ #
366
+ # proxy = URI 'http://proxy.example'
367
+ # proxy.user = 'AzureDiamond'
368
+ # proxy.password = 'hunter2'
369
+
370
+ def initialize name = nil, proxy = nil
371
+ @name = name
372
+
373
+ @debug_output = nil
374
+ @proxy_uri = nil
375
+ @headers = {}
376
+ @override_headers = {}
377
+ @http_versions = {}
378
+ @keep_alive = 30
379
+ @open_timeout = nil
380
+ @read_timeout = nil
381
+ @idle_timeout = 5
382
+ @socket_options = []
383
+
384
+ @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
385
+ Socket.const_defined? :TCP_NODELAY
386
+
387
+ key = ['net_http_persistent', name].compact
388
+ @generation_key = [key, 'generations' ].join('_').intern
389
+ @ssl_generation_key = [key, 'ssl_generations'].join('_').intern
390
+ @request_key = [key, 'requests' ].join('_').intern
391
+ @timeout_key = [key, 'timeouts' ].join('_').intern
392
+
393
+ @certificate = nil
394
+ @ca_file = nil
395
+ @private_key = nil
396
+ @ssl_version = nil
397
+ @verify_callback = nil
398
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
399
+ @cert_store = nil
400
+
401
+ @generation = 0 # incremented when proxy URI changes
402
+ @ssl_generation = 0 # incremented when SSL session variables change
403
+ @reuse_ssl_sessions = true
404
+
405
+ @retry_change_requests = false
406
+
407
+ self.proxy = proxy if proxy
408
+ end
409
+
410
+ ##
411
+ # Sets this client's OpenSSL::X509::Certificate
412
+
413
+ def certificate= certificate
414
+ @certificate = certificate
415
+
416
+ reconnect_ssl
417
+ end
418
+
419
+ ##
420
+ # Sets the SSL certificate authority file.
421
+
422
+ def ca_file= file
423
+ @ca_file = file
424
+
425
+ reconnect_ssl
426
+ end
427
+
428
+ ##
429
+ # Overrides the default SSL certificate store used for verifying
430
+ # connections.
431
+
432
+ def cert_store= store
433
+ @cert_store = store
434
+
435
+ reconnect_ssl
436
+ end
437
+
438
+ ##
439
+ # Finishes all connections on the given +thread+ that were created before
440
+ # the given +generation+ in the threads +generation_key+ list.
441
+ #
442
+ # See #shutdown for a bunch of scary warning about misusing this method.
443
+
444
+ def cleanup(generation, thread = Thread.current,
445
+ generation_key = @generation_key) # :nodoc:
446
+ timeouts = thread[@timeout_key]
447
+
448
+ (0...generation).each do |old_generation|
449
+ conns = thread[generation_key].delete old_generation
450
+
451
+ conns.each_value do |conn|
452
+ finish conn, thread
453
+
454
+ timeouts.delete conn.object_id if timeouts
455
+ end if conns
456
+ end
457
+ end
458
+
459
+ ##
460
+ # Creates a new connection for +uri+
461
+
462
+ def connection_for uri
463
+ Thread.current[@generation_key] ||= Hash.new { |h,k| h[k] = {} }
464
+ Thread.current[@ssl_generation_key] ||= Hash.new { |h,k| h[k] = {} }
465
+ Thread.current[@request_key] ||= Hash.new 0
466
+ Thread.current[@timeout_key] ||= Hash.new EPOCH
467
+
468
+ use_ssl = uri.scheme.downcase == 'https'
469
+
470
+ if use_ssl then
471
+ ssl_generation = @ssl_generation
472
+
473
+ ssl_cleanup ssl_generation
474
+
475
+ connections = Thread.current[@ssl_generation_key][ssl_generation]
476
+ else
477
+ generation = @generation
478
+
479
+ cleanup generation
480
+
481
+ connections = Thread.current[@generation_key][generation]
482
+ end
483
+
484
+ net_http_args = [uri.host, uri.port]
485
+ connection_id = net_http_args.join ':'
486
+
487
+ if @proxy_uri then
488
+ connection_id << @proxy_connection_id
489
+ net_http_args.concat @proxy_args
490
+ end
491
+
492
+ connection = connections[connection_id]
493
+
494
+ unless connection = connections[connection_id] then
495
+ connections[connection_id] = http_class.new(*net_http_args)
496
+ connection = connections[connection_id]
497
+ ssl connection if use_ssl
498
+ else
499
+ last_used = Thread.current[@timeout_key][connection.object_id]
500
+ reset connection unless last_used > max_age
501
+ end
502
+
503
+ unless connection.started? then
504
+ connection.set_debug_output @debug_output if @debug_output
505
+ connection.open_timeout = @open_timeout if @open_timeout
506
+ connection.read_timeout = @read_timeout if @read_timeout
507
+
508
+ connection.start
509
+
510
+ socket = connection.instance_variable_get :@socket
511
+
512
+ if socket then # for fakeweb
513
+ @socket_options.each do |option|
514
+ socket.io.setsockopt(*option)
515
+ end
516
+ end
517
+ end
518
+
519
+ connection
520
+ rescue Errno::ECONNREFUSED
521
+ raise Error, "connection refused: #{connection.address}:#{connection.port}"
522
+ rescue Errno::EHOSTDOWN
523
+ raise Error, "host down: #{connection.address}:#{connection.port}"
524
+ end
525
+
526
+ ##
527
+ # Returns an error message containing the number of requests performed on
528
+ # this connection
529
+
530
+ def error_message connection
531
+ requests = Thread.current[@request_key][connection.object_id] - 1 # fixup
532
+ last_use = Thread.current[@timeout_key][connection.object_id]
533
+
534
+ age = Time.now - last_use
535
+
536
+ "after #{requests} requests on #{connection.object_id}, " \
537
+ "last used #{age} seconds ago"
538
+ end
539
+
540
+ ##
541
+ # URI::escape wrapper
542
+
543
+ def escape str
544
+ CGI.escape str if str
545
+ end
546
+
547
+ ##
548
+ # Finishes the Net::HTTP +connection+
549
+
550
+ def finish connection, thread = Thread.current
551
+ if requests = thread[@request_key] then
552
+ requests.delete connection.object_id
553
+ end
554
+
555
+ connection.finish
556
+ rescue IOError
557
+ end
558
+
559
+ def http_class # :nodoc:
560
+ if [:FakeWeb, :WebMock].any? { |klass| Object.const_defined?(klass) } or
561
+ not @reuse_ssl_sessions then
562
+ Net::HTTP
563
+ else
564
+ Net::HTTP::Persistent::SSLReuse
565
+ end
566
+ end
567
+
568
+ ##
569
+ # Returns the HTTP protocol version for +uri+
570
+
571
+ def http_version uri
572
+ @http_versions["#{uri.host}:#{uri.port}"]
573
+ end
574
+
575
+ ##
576
+ # Is +req+ idempotent according to RFC 2616?
577
+
578
+ def idempotent? req
579
+ case req
580
+ when Net::HTTP::Delete, Net::HTTP::Get, Net::HTTP::Head,
581
+ Net::HTTP::Options, Net::HTTP::Put, Net::HTTP::Trace then
582
+ true
583
+ end
584
+ end
585
+
586
+ ##
587
+ # Is the request idempotent or is retry_change_requests allowed
588
+
589
+ def can_retry? req
590
+ retry_change_requests or idempotent?(req)
591
+ end
592
+
593
+ if RUBY_VERSION > '1.9' then
594
+ ##
595
+ # Workaround for missing Net::HTTPHeader#connection_close? on Ruby 1.8
596
+
597
+ def connection_close? header
598
+ header.connection_close?
599
+ end
600
+
601
+ ##
602
+ # Workaround for missing Net::HTTPHeader#connection_keep_alive? on Ruby 1.8
603
+
604
+ def connection_keep_alive? header
605
+ header.connection_keep_alive?
606
+ end
607
+ else
608
+ ##
609
+ # Workaround for missing Net::HTTPRequest#connection_close? on Ruby 1.8
610
+
611
+ def connection_close? header
612
+ header['connection'] =~ /close/ or header['proxy-connection'] =~ /close/
613
+ end
614
+
615
+ ##
616
+ # Workaround for missing Net::HTTPRequest#connection_keep_alive? on Ruby
617
+ # 1.8
618
+
619
+ def connection_keep_alive? header
620
+ header['connection'] =~ /keep-alive/ or
621
+ header['proxy-connection'] =~ /keep-alive/
622
+ end
623
+ end
624
+
625
+ ##
626
+ # If a connection hasn't been used since max_age it will be reset and reused
627
+
628
+ def max_age
629
+ Time.now - @idle_timeout
630
+ end
631
+
632
+ ##
633
+ # Adds "http://" to the String +uri+ if it is missing.
634
+
635
+ def normalize_uri uri
636
+ (uri =~ /^https?:/) ? uri : "http://#{uri}"
637
+ end
638
+
639
+ ##
640
+ # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a
641
+ # block is given. Returns all responses recieved.
642
+ #
643
+ # See
644
+ # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html]
645
+ # for further details.
646
+ #
647
+ # Only if <tt>net-http-pipeline</tt> was required before
648
+ # <tt>net-http-persistent</tt> #pipeline will be present.
649
+
650
+ def pipeline uri, requests, &block # :yields: responses
651
+ connection = connection_for uri
652
+
653
+ connection.pipeline requests, &block
654
+ end
655
+
656
+ ##
657
+ # Sets this client's SSL private key
658
+
659
+ def private_key= key
660
+ @private_key = key
661
+
662
+ reconnect_ssl
663
+ end
664
+
665
+ ##
666
+ # Sets the proxy server. The +proxy+ may be the URI of the proxy server,
667
+ # the symbol +:ENV+ which will read the proxy from the environment or nil to
668
+ # disable use of a proxy. See #proxy_from_env for details on setting the
669
+ # proxy from the environment.
670
+ #
671
+ # If the proxy URI is set after requests have been made, the next request
672
+ # will shut-down and re-open all connections.
673
+ #
674
+ # If you are making some requests through a proxy and others without a proxy
675
+ # use separate Net::Http::Persistent instances.
676
+
677
+ def proxy= proxy
678
+ @proxy_uri = case proxy
679
+ when :ENV then proxy_from_env
680
+ when URI::HTTP then proxy
681
+ when nil then # ignore
682
+ else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
683
+ end
684
+
685
+ if @proxy_uri then
686
+ @proxy_args = [
687
+ @proxy_uri.host,
688
+ @proxy_uri.port,
689
+ @proxy_uri.user,
690
+ @proxy_uri.password,
691
+ ]
692
+
693
+ @proxy_connection_id = [nil, *@proxy_args].join ':'
694
+ end
695
+
696
+ reconnect
697
+ reconnect_ssl
698
+ end
699
+
700
+ ##
701
+ # Creates a URI for an HTTP proxy server from ENV variables.
702
+ #
703
+ # If +HTTP_PROXY+ is set a proxy will be returned.
704
+ #
705
+ # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the URI is given the
706
+ # indicated user and password unless HTTP_PROXY contains either of these in
707
+ # the URI.
708
+ #
709
+ # For Windows users, lowercase ENV variables are preferred over uppercase ENV
710
+ # variables.
711
+
712
+ def proxy_from_env
713
+ env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
714
+
715
+ return nil if env_proxy.nil? or env_proxy.empty?
716
+
717
+ uri = URI normalize_uri env_proxy
718
+
719
+ unless uri.user or uri.password then
720
+ uri.user = escape ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']
721
+ uri.password = escape ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']
722
+ end
723
+
724
+ uri
725
+ end
726
+
727
+ ##
728
+ # Forces reconnection of HTTP connections.
729
+
730
+ def reconnect
731
+ @generation += 1
732
+ end
733
+
734
+ ##
735
+ # Forces reconnection of SSL connections.
736
+
737
+ def reconnect_ssl
738
+ @ssl_generation += 1
739
+ end
740
+
741
+ ##
742
+ # Finishes then restarts the Net::HTTP +connection+
743
+
744
+ def reset connection
745
+ Thread.current[@request_key].delete connection.object_id
746
+ Thread.current[@timeout_key].delete connection.object_id
747
+
748
+ finish connection
749
+
750
+ connection.start
751
+ rescue Errno::ECONNREFUSED
752
+ raise Error, "connection refused: #{connection.address}:#{connection.port}"
753
+ rescue Errno::EHOSTDOWN
754
+ raise Error, "host down: #{connection.address}:#{connection.port}"
755
+ end
756
+
757
+ ##
758
+ # Makes a request on +uri+. If +req+ is nil a Net::HTTP::Get is performed
759
+ # against +uri+.
760
+ #
761
+ # If a block is passed #request behaves like Net::HTTP#request (the body of
762
+ # the response will not have been read).
763
+ #
764
+ # +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list).
765
+ #
766
+ # If there is an error and the request is idempontent according to RFC 2616
767
+ # it will be retried automatically.
768
+
769
+ def request uri, req = nil, &block
770
+ retried = false
771
+ bad_response = false
772
+
773
+ req = Net::HTTP::Get.new uri.request_uri unless req
774
+
775
+ @headers.each do |pair|
776
+ req.add_field(*pair)
777
+ end
778
+
779
+ @override_headers.each do |name, value|
780
+ req[name] = value
781
+ end
782
+
783
+ unless req['Connection'] then
784
+ req.add_field 'Connection', 'keep-alive'
785
+ req.add_field 'Keep-Alive', @keep_alive
786
+ end
787
+
788
+ connection = connection_for uri
789
+ connection_id = connection.object_id
790
+
791
+ begin
792
+ Thread.current[@request_key][connection_id] += 1
793
+ response = connection.request req, &block
794
+
795
+ if connection_close?(req) or
796
+ (response.http_version <= '1.0' and
797
+ not connection_keep_alive?(response)) or
798
+ connection_close?(response) then
799
+ connection.finish
800
+ end
801
+ rescue Net::HTTPBadResponse => e
802
+ message = error_message connection
803
+
804
+ finish connection
805
+
806
+ raise Error, "too many bad responses #{message}" if
807
+ bad_response or not can_retry? req
808
+
809
+ bad_response = true
810
+ retry
811
+ rescue IOError, EOFError, Timeout::Error,
812
+ Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE,
813
+ Errno::EINVAL, OpenSSL::SSL::SSLError => e
814
+
815
+ if retried or not can_retry? req
816
+ due_to = "(due to #{e.message} - #{e.class})"
817
+ message = error_message connection
818
+
819
+ finish connection
820
+
821
+ raise Error, "too many connection resets #{due_to} #{message}"
822
+ end
823
+
824
+ reset connection
825
+
826
+ retried = true
827
+ retry
828
+ ensure
829
+ Thread.current[@timeout_key][connection_id] = Time.now
830
+ end
831
+
832
+ @http_versions["#{uri.host}:#{uri.port}"] ||= response.http_version
833
+
834
+ response
835
+ end
836
+
837
+ ##
838
+ # Shuts down all connections for +thread+.
839
+ #
840
+ # Uses the current thread by default.
841
+ #
842
+ # If you've used Net::HTTP::Persistent across multiple threads you should
843
+ # call this in each thread when you're done making HTTP requests.
844
+ #
845
+ # *NOTE*: Calling shutdown for another thread can be dangerous!
846
+ #
847
+ # If the thread is still using the connection it may cause an error! It is
848
+ # best to call #shutdown in the thread at the appropriate time instead!
849
+
850
+ def shutdown thread = Thread.current
851
+ generation = reconnect
852
+ cleanup generation, thread, @generation_key
853
+
854
+ ssl_generation = reconnect_ssl
855
+ cleanup ssl_generation, thread, @ssl_generation_key
856
+
857
+ thread[@request_key] = nil
858
+ thread[@timeout_key] = nil
859
+ end
860
+
861
+ ##
862
+ # Shuts down all connections in all threads
863
+ #
864
+ # *NOTE*: THIS METHOD IS VERY DANGEROUS!
865
+ #
866
+ # Do not call this method if other threads are still using their
867
+ # connections! Call #shutdown at the appropriate time instead!
868
+ #
869
+ # Use this method only as a last resort!
870
+
871
+ def shutdown_in_all_threads
872
+ Thread.list.each do |thread|
873
+ shutdown thread
874
+ end
875
+
876
+ nil
877
+ end
878
+
879
+ ##
880
+ # Enables SSL on +connection+
881
+
882
+ def ssl connection
883
+ connection.use_ssl = true
884
+
885
+ connection.ssl_version = @ssl_version if @ssl_version
886
+
887
+ connection.verify_mode = @verify_mode
888
+
889
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
890
+ not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
891
+ warn <<-WARNING
892
+ !!!SECURITY WARNING!!!
893
+
894
+ The SSL HTTP connection to:
895
+
896
+ #{connection.address}:#{connection.port}
897
+
898
+ !!!MAY NOT BE VERIFIED!!!
899
+
900
+ On your platform your OpenSSL implementation is broken.
901
+
902
+ There is no difference between the values of VERIFY_NONE and VERIFY_PEER.
903
+
904
+ This means that attempting to verify the security of SSL connections may not
905
+ work. This exposes you to man-in-the-middle exploits, snooping on the
906
+ contents of your connection and other dangers to the security of your data.
907
+
908
+ To disable this warning define the following constant at top-level in your
909
+ application:
910
+
911
+ I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
912
+
913
+ WARNING
914
+ end
915
+
916
+ if @ca_file then
917
+ connection.ca_file = @ca_file
918
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
919
+ connection.verify_callback = @verify_callback if @verify_callback
920
+ end
921
+
922
+ if @certificate and @private_key then
923
+ connection.cert = @certificate
924
+ connection.key = @private_key
925
+ end
926
+
927
+ connection.cert_store = if @cert_store then
928
+ @cert_store
929
+ else
930
+ store = OpenSSL::X509::Store.new
931
+ store.set_default_paths
932
+ store
933
+ end
934
+ end
935
+
936
+ ##
937
+ # Finishes all connections that existed before the given SSL parameter
938
+ # +generation+.
939
+
940
+ def ssl_cleanup generation # :nodoc:
941
+ cleanup generation, Thread.current, @ssl_generation_key
942
+ end
943
+
944
+ ##
945
+ # SSL version to use
946
+
947
+ def ssl_version= ssl_version
948
+ @ssl_version = ssl_version
949
+
950
+ reconnect_ssl
951
+ end if RUBY_VERSION > '1.9'
952
+
953
+ ##
954
+ # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER.
955
+ #
956
+ # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used.
957
+ # Securely transfer the correct certificate and update the default
958
+ # certificate store or set the ca file instead.
959
+
960
+ def verify_mode= verify_mode
961
+ @verify_mode = verify_mode
962
+
963
+ reconnect_ssl
964
+ end
965
+
966
+ ##
967
+ # SSL verification callback.
968
+
969
+ def verify_callback= callback
970
+ @verify_callback = callback
971
+
972
+ reconnect_ssl
973
+ end
974
+
975
+ end
976
+
977
+ require 'net/http/persistent/ssl_reuse'
978
+