gem 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+