Class: PersistentHTTP::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/persistent_http/connection.rb

Constant Summary collapse

RETRIED_EXCEPTIONS =

Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with the exception list for ruby 1.x.

[ # :nodoc:
  IOError,
  EOFError,
  Errno::ECONNRESET,
  Errno::ECONNABORTED,
  Errno::EPIPE,
  (OpenSSL::SSL::SSLError if defined? OpenSSL::SSL),
].compact

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Connection

Creates a new HTTP Connection.

Set name to keep your connections apart from everybody else’s. Not required currently, but highly recommended. Your library name should be good enough. This parameter will be required in a future version.

proxy may be set to a URI::HTTP or :ENV to pick up proxy options from the environment. See proxy_from_env for details.

In order to use a URI for the proxy you’ll need to do some extra work beyond URI.parse:

proxy = URI.parse 'http://proxy.example'
proxy.user     = 'AzureDiamond'
proxy.password = 'hunter2'


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/persistent_http/connection.rb', line 154

def initialize(options={})
  @name            = options[:name]            || 'PeristentHTTP::Connection'
  @ca_file         = options[:ca_file]
  @certificate     = options[:certificate]
  @debug_output    = options[:debug_output]
  @default_path    = options[:default_path]
  @force_retry     = options[:force_retry]
  @headers         = options[:header]          || {}
  @host            = options[:host]
  @keep_alive      = options[:keep_alive]      || 30
  @logger          = options[:logger]
  @port            = options[:port]
  @private_key     = options[:private_key]
  @open_timeout    = options[:open_timeout]
  @read_timeout    = options[:read_timeout]
  @use_ssl         = options[:use_ssl]
  @verify_callback = options[:verify_callback]
  @verify_mode     = options[:verify_mode]
  @after_connect   = options[:after_connect]
  # Because maybe we want a non-persistent connection and are just using this for the proxy stuff
  @non_persistent  = options[:non_persistent]

  url              = options[:url]
  if url
    url = URI.parse(url) if url.kind_of? String
    @default_path ||= url.request_uri
    @host         ||= url.host
    @port         ||= url.port
    @use_ssl      ||= url.scheme == 'https'
  end

  @port ||= (@use_ssl ? 443 : 80)

  raise 'host not set' unless @host
  @net_http_args = [@host, @port]

  proxy = options[:proxy]
  @proxy_uri = case proxy
               when :ENV      then proxy_from_env
               when URI::HTTP then proxy
               when nil       then # ignore
               else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
               end

  if @proxy_uri then
    @proxy_args = [
      @proxy_uri.host,
      @proxy_uri.port,
      @proxy_uri.user,
      @proxy_uri.password,
    ]

    @net_http_args.concat @proxy_args
  end

  @name += ':' + @net_http_args.join(':')
  @logger.debug { "#{@name}: Creating connection" } if @logger
  renew
end

Instance Attribute Details

#after_connectObject

Proc to execute after connect which will get passed the Net::HTTP connection



135
136
137
# File 'lib/persistent_http/connection.rb', line 135

def after_connect
  @after_connect
end

#ca_fileObject

An SSL certificate authority. Setting this will set verify_mode to VERIFY_PEER.



46
47
48
# File 'lib/persistent_http/connection.rb', line 46

def ca_file
  @ca_file
end

#certificateObject

This client’s OpenSSL::X509::Certificate



50
51
52
# File 'lib/persistent_http/connection.rb', line 50

def certificate
  @certificate
end

#debug_outputObject

Sends debug_output to this IO via Net::HTTP#set_debug_output.

Never use this method in production code, it causes a serious security hole.



57
58
59
# File 'lib/persistent_http/connection.rb', line 57

def debug_output
  @debug_output
end

#default_pathObject

Default path for the request



61
62
63
# File 'lib/persistent_http/connection.rb', line 61

def default_path
  @default_path
end

#force_retryObject

Retry even for non-idempotent (POST) requests.



65
66
67
# File 'lib/persistent_http/connection.rb', line 65

def force_retry
  @force_retry
end

#headersObject

Headers that are added to every request



69
70
71
# File 'lib/persistent_http/connection.rb', line 69

def headers
  @headers
end

#hostObject (readonly)

Host for the Net:HTTP connection



73
74
75
# File 'lib/persistent_http/connection.rb', line 73

def host
  @host
end

#http_versionObject (readonly)

HTTP version to enable version specific features.



77
78
79
# File 'lib/persistent_http/connection.rb', line 77

def http_version
  @http_version
end

#keep_aliveObject

The value sent in the Keep-Alive header. Defaults to 30. Not needed for HTTP/1.1 servers.

This may not work correctly for HTTP/1.0 servers

This method may be removed in a future version as RFC 2616 does not require this header.



87
88
89
# File 'lib/persistent_http/connection.rb', line 87

def keep_alive
  @keep_alive
end

#loggerObject

Logger for message logging.



91
92
93
# File 'lib/persistent_http/connection.rb', line 91

def logger
  @logger
end

#nameObject (readonly)

A name for this connection. Allows you to keep your connections apart from everybody else’s.



96
97
98
# File 'lib/persistent_http/connection.rb', line 96

def name
  @name
end

#open_timeoutObject

Seconds to wait until a connection is opened. See Net::HTTP#open_timeout



100
101
102
# File 'lib/persistent_http/connection.rb', line 100

def open_timeout
  @open_timeout
end

#portObject (readonly)

Port for the Net:HTTP connection



104
105
106
# File 'lib/persistent_http/connection.rb', line 104

def port
  @port
end

#private_keyObject

This client’s SSL private key



108
109
110
# File 'lib/persistent_http/connection.rb', line 108

def private_key
  @private_key
end

#proxy_uriObject (readonly)

The URL through which requests will be proxied



112
113
114
# File 'lib/persistent_http/connection.rb', line 112

def proxy_uri
  @proxy_uri
end

#read_timeoutObject

Seconds to wait until reading one block. See Net::HTTP#read_timeout



116
117
118
# File 'lib/persistent_http/connection.rb', line 116

def read_timeout
  @read_timeout
end

#use_sslObject (readonly)

Use ssl if set



120
121
122
# File 'lib/persistent_http/connection.rb', line 120

def use_ssl
  @use_ssl
end

#verify_callbackObject

SSL verification callback. Used when ca_file is set.



124
125
126
# File 'lib/persistent_http/connection.rb', line 124

def verify_callback
  @verify_callback
end

#verify_modeObject

HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_NONE which ignores certificate problems.

You can use verify_mode to override any default values.



131
132
133
# File 'lib/persistent_http/connection.rb', line 131

def verify_mode
  @verify_mode
end

Instance Method Details

#finishObject

Finishes the Net::HTTP connection



306
307
308
309
# File 'lib/persistent_http/connection.rb', line 306

def finish
  @connection.finish
rescue IOError
end

#renewObject



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/persistent_http/connection.rb', line 214

def renew
  finish if @connection
  @message_count = 0
  @connection = Net::HTTP.new(*@net_http_args)
  @connection.set_debug_output @debug_output if @debug_output
  @connection.open_timeout = @open_timeout if @open_timeout
  @connection.read_timeout = @read_timeout if @read_timeout
  @connection.keep_alive_timeout = @keep_alive if @keep_alive

  ssl if @use_ssl

  @connection.start
  @logger.debug { "#{@name} #{@connection}: Connection created" } if @logger
  @after_connect.call(@connection) if @after_connect
rescue Errno::ECONNREFUSED
  raise Error, "connection refused: #{@connection.address}:#{@connection.port}"
rescue Errno::EHOSTDOWN
  raise Error, "host down: #{@connection.address}:#{@connection.port}"
end

#request(req = nil, options = {}, &block) ⇒ Object

Makes a request per req. If req is nil a Net::HTTP::Get is performed against default_path.

If a block is passed #request behaves like Net::HTTP#request (the body of the response will not have been read).

req must be a Net::HTTPRequest subclass (see Net::HTTP for a list).

If there is an error and the request is idempontent according to RFC 2616 it will be retried automatically.



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/persistent_http/connection.rb', line 246

def request(req = nil, options = {}, &block)
  retried      = false
  bad_response = false

  req = Net::HTTP::Get.new @default_path unless req

  headers.each do |pair|
    req.add_field(*pair)
  end

  unless @non_persistent
    req.add_field 'Connection', 'keep-alive'
    req.add_field 'Keep-Alive', @keep_alive
  end

  begin
    options.each do |key, value|
      @connection.send("#{key}=", value)
    end
    response = @connection.request req, &block
    @http_version ||= response.http_version
    @message_count += 1
    return response

  rescue Timeout::Error => e
    due_to = "(due to #{e.message} - #{e.class})"
    @logger.info "#{@name}: Removing connection #{due_to} #{error_message}" if @logger
    finish
    raise

  rescue Net::HTTPBadResponse => e
    if bad_response or not (idempotent? req or @force_retry)
      @logger.info "#{@name}: Removing connection because of too many bad responses #{error_message}" if @logger
      finish
      raise Error, "too many bad responses #{error_message}"
    else
      bad_response = true
      @logger.info "#{@name}: Renewing connection because of bad response #{error_message}" if @logger
      renew
      retry
    end

  rescue *RETRIED_EXCEPTIONS => e
    due_to = "(due to #{e.message} - #{e.class})"
    if retried or not (idempotent? req or @force_retry)
      @logger.info "#{@name}: Removing connection #{due_to} #{error_message}" if @logger
      finish
      raise Error, "too many connection resets #{due_to} #{error_message}"
    else
      retried = true
      @logger.info "#{@name}: Renewing connection #{due_to} #{error_message}" if @logger
      renew
      retry
    end
  end
end

#to_sObject



311
312
313
# File 'lib/persistent_http/connection.rb', line 311

def to_s
  @name
end