right_http_connection 1.3.0 → 1.4.0

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.
@@ -57,6 +57,30 @@ Initial public release
57
57
  * fixed a bug: <NoMethodError: You have a nil object when you didn't expect it!
58
58
  The error occurred while evaluating nil.body_stream>
59
59
 
60
- == 1.2.5 (not released yet)
60
+ == 1.2.5
61
61
 
62
62
  - ActiveSupport dependency removal
63
+
64
+
65
+ == 1.3.0
66
+ - Added:
67
+ - support for using through proxies
68
+ - functional tests
69
+
70
+ == 1.3.1
71
+ - Added:
72
+ - SSL certificate handshake support
73
+ - more specs
74
+ - ability to give client side key and certificate inline
75
+ - Fixed: some minor glitches
76
+
77
+ == 1.4.0
78
+ - Added
79
+ - license
80
+ - HTTP_PROXY env variable support
81
+ - Fixed
82
+ - context-length issue (thx kristianm)
83
+ - exception handling
84
+ - connection is now closed on any exception
85
+ - connection is reestablished when credentials change
86
+ - some other minor bugs
@@ -91,18 +91,30 @@ module Net
91
91
  private
92
92
 
93
93
  def send_request_with_body(sock, ver, path, body, send_only=nil)
94
- self.content_length = body.length
94
+ self.content_length = body.respond_to?(:bytesize) ? body.bytesize : body.length
95
95
  delete 'Transfer-Encoding'
96
96
  supply_default_content_type
97
97
  write_header(sock, ver, path) unless send_only == :body
98
- sock.write(body) unless send_only == :header
98
+ sock.write(body && body.to_s) unless send_only == :header
99
99
  end
100
100
 
101
101
  def send_request_with_body_stream(sock, ver, path, f, send_only=nil)
102
+ # KD: Fix 'content-length': it must not be greater than a piece of file left to be read.
103
+ # Otherwise the connection may behave like crazy causing 4xx or 5xx responses
104
+ #
105
+ # Only do this helpful thing if the stream responds to :pos (it may be something
106
+ # that responds to :read and :size but not :pos).
107
+ if f.respond_to?(:pos)
108
+ file_size = f.respond_to?(:lstat) ? f.lstat.size : f.size
109
+ bytes_to_read = [ file_size - f.pos, self.content_length.to_i ].sort.first
110
+ self.content_length = bytes_to_read
111
+ end
112
+
102
113
  unless content_length() or chunked?
103
114
  raise ArgumentError,
104
115
  "Content-Length not given and Transfer-Encoding is not `chunked'"
105
116
  end
117
+ bytes_to_read ||= content_length()
106
118
  supply_default_content_type
107
119
  write_header(sock, ver, path) unless send_only == :body
108
120
  unless send_only == :header
@@ -112,8 +124,14 @@ module Net
112
124
  end
113
125
  sock.write "0\r\n\r\n"
114
126
  else
115
- while s = f.read(@@local_read_size)
127
+ # KD: When we read/write over file EOF it sometimes make the connection unstable
128
+ read_size = [ @@local_read_size, bytes_to_read ].sort.first
129
+ while s = f.read(read_size)
116
130
  sock.write s
131
+ # Make sure we do not read over EOF or more than expected content-length
132
+ bytes_to_read -= read_size
133
+ break if bytes_to_read <= 0
134
+ read_size = bytes_to_read if bytes_to_read < read_size
117
135
  end
118
136
  end
119
137
  end
File without changes
@@ -24,7 +24,7 @@
24
24
  module RightHttpConnection #:nodoc:
25
25
  module VERSION #:nodoc:
26
26
  MAJOR = 1 unless defined?(MAJOR)
27
- MINOR = 3 unless defined?(MINOR)
27
+ MINOR = 4 unless defined?(MINOR)
28
28
  TINY = 0 unless defined?(TINY)
29
29
 
30
30
  STRING = [MAJOR, MINOR, TINY].join('.') unless defined?(STRING)
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2007-2008 RightScale Inc
2
+ # Copyright (c) 2007-2011 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -27,9 +27,9 @@ require "time"
27
27
  require "logger"
28
28
 
29
29
  $:.unshift(File.dirname(__FILE__))
30
- require 'version'
31
- require 'support'
32
- require "net_fix"
30
+ require 'base/version'
31
+ require 'base/support'
32
+ require 'base/net_fix'
33
33
 
34
34
  module Rightscale
35
35
 
@@ -137,26 +137,47 @@ them.
137
137
  # :raise_on_timeout # do not perform a retry if timeout is received (false by default)
138
138
  def initialize(params={})
139
139
  @params = params
140
+
141
+ #set up logging first
142
+ @logger = get_param(:logger) ||
143
+ (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
144
+ Logger.new(STDOUT)
145
+
146
+ env_proxy_host, env_proxy_port, env_proxy_username, env_proxy_password = get_proxy_info_for_env if ENV['HTTP_PROXY']
147
+
140
148
  @params[:http_connection_retry_count] ||= @@params[:http_connection_retry_count]
141
149
  @params[:http_connection_open_timeout] ||= @@params[:http_connection_open_timeout]
142
150
  @params[:http_connection_read_timeout] ||= @@params[:http_connection_read_timeout]
143
151
  @params[:http_connection_retry_delay] ||= @@params[:http_connection_retry_delay]
144
- @params[:proxy_host] ||= @@params[:proxy_host]
145
- @params[:proxy_port] ||= @@params[:proxy_port]
146
- @params[:proxy_username] ||= @@params[:proxy_username]
147
- @params[:proxy_password] ||= @@params[:proxy_password]
152
+ @params[:proxy_host] ||= @@params[:proxy_host] || env_proxy_host
153
+ @params[:proxy_port] ||= @@params[:proxy_port] || env_proxy_port
154
+ @params[:proxy_username] ||= @@params[:proxy_username] || env_proxy_username
155
+ @params[:proxy_password] ||= @@params[:proxy_password] || env_proxy_password
156
+
148
157
  @http = nil
149
158
  @server = nil
150
- @logger = get_param(:logger) ||
151
- (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
152
- Logger.new(STDOUT)
153
159
  #--------------
154
160
  # Retry state - Keep track of errors on a per-server basis
155
161
  #--------------
156
162
  @state = {} # retry state indexed by server: consecutive error count, error time, and error
163
+
157
164
  @eof = {}
158
165
  end
159
166
 
167
+ def get_proxy_info_for_env
168
+ parsed_uri = URI.parse(ENV['HTTP_PROXY'])
169
+ if parsed_uri.scheme.to_s.downcase == 'http'
170
+ return parsed_uri.host, parsed_uri.port, parsed_uri.user, parsed_uri.password
171
+ else
172
+ @logger.warn "Invalid protocol in ENV['HTTP_PROXY'] URI = #{ENV['HTTP_PROXY'].inspect} expecting 'http' got #{parsed_uri.scheme.inspect}"
173
+ return
174
+ end
175
+ rescue Exception => e
176
+ @logger.warn "Error parsing ENV['HTTP_PROXY'] with exception: #{e.message}"
177
+ return
178
+ end
179
+ private :get_proxy_info_for_env
180
+
160
181
  def get_param(name, custom_options={})
161
182
  custom_options [name] || @params[name] || @@params[name]
162
183
  end
@@ -207,7 +228,9 @@ them.
207
228
  end
208
229
 
209
230
  # add an error for a server
210
- def error_add(message)
231
+ def error_add(error)
232
+ message = error
233
+ message = "#{error.class.name}: #{error.message}" if error.is_a?(Exception)
211
234
  @state[@server] = { :count => error_count+1, :time => Time.now, :message => message }
212
235
  end
213
236
 
@@ -218,7 +241,7 @@ them.
218
241
 
219
242
  # Error message stuff...
220
243
  def banana_message
221
- return "#{@server} temporarily unavailable: (#{error_message})"
244
+ return "#{@protocol}://#{@server}:#{@port} temporarily unavailable: (#{error_message})"
222
245
  end
223
246
 
224
247
  def err_header
@@ -280,6 +303,8 @@ them.
280
303
  end
281
304
  end
282
305
 
306
+ SECURITY_PARAMS = [:cert, :key, :cert_file, :key_file, :ca_file]
307
+
283
308
  # Start a fresh connection. The object closes any existing connection and
284
309
  # opens a new one.
285
310
  def start(request_params)
@@ -293,10 +318,15 @@ them.
293
318
  @proxy_port = request_params[:proxy_port]
294
319
  @proxy_username = request_params[:proxy_username]
295
320
  @proxy_password = request_params[:proxy_password]
321
+
322
+ SECURITY_PARAMS.each do |param_name|
323
+ @params[param_name] = request_params[param_name]
324
+ end
296
325
 
297
326
  @logger.info("Opening new #{@protocol.upcase} connection to #@server:#@port")
327
+
298
328
  @logger.info("Connecting to proxy #{@proxy_host}:#{@proxy_port} with username" +
299
- " #{@proxy_username}") unless @proxy_host.nil?
329
+ " #{@proxy_username.inspect}") unless @proxy_host.nil?
300
330
 
301
331
  @http = Net::HTTP.new(@server, @port, @proxy_host, @proxy_port, @proxy_username,
302
332
  @proxy_password)
@@ -305,10 +335,9 @@ them.
305
335
 
306
336
  if @protocol == 'https'
307
337
  verifyCallbackProc = Proc.new{ |ok, x509_store_ctx|
338
+ # List of error codes: http://www.openssl.org/docs/apps/verify.html
308
339
  code = x509_store_ctx.error
309
340
  msg = x509_store_ctx.error_string
310
- #debugger
311
- @logger.warn("##### #{@server} certificate verify failed: #{msg}") unless code == 0
312
341
  if request_params[:fail_if_ca_mismatch] && code != 0
313
342
  false
314
343
  else
@@ -316,14 +345,44 @@ them.
316
345
  end
317
346
  }
318
347
  @http.use_ssl = true
348
+
319
349
  ca_file = get_param(:ca_file)
320
- if ca_file
321
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
350
+ if ca_file && File.exists?(ca_file)
351
+ # Documentation for 'http.rb':
352
+ # : verify_mode, verify_mode=((|mode|))
353
+ # Sets the flags for server the certification verification at
354
+ # beginning of SSL/TLS session.
355
+ # OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable.
356
+ #
357
+ # KHRVI: looks like the constant VERIFY_FAIL_IF_NO_PEER_CERT is not acceptable
322
358
  @http.verify_callback = verifyCallbackProc
323
- @http.ca_file = ca_file
359
+ @http.ca_file= ca_file
360
+ @http.verify_mode = get_param(:use_server_auth) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
361
+ # The depth count is 'level 0:peer certificate', 'level 1: CA certificate', 'level 2: higher level CA certificate', and so on.
362
+ # Setting the maximum depth to 2 allows the levels 0, 1, and 2. The default depth limit is 9, allowing for the peer certificate and additional 9 CA certificates.
363
+ @http.verify_depth = 9
324
364
  else
325
365
  @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
326
366
  end
367
+
368
+ # CERT
369
+ cert_file = get_param(:cert_file, request_params)
370
+ cert = File.read(cert_file) if cert_file && File.exists?(cert_file)
371
+ cert ||= get_param(:cert, request_params)
372
+ # KEY
373
+ key_file = get_param(:key_file, request_params)
374
+ key = File.read(key_file) if key_file && File.exists?(key_file)
375
+ key ||= get_param(:key, request_params)
376
+ if cert && key
377
+ begin
378
+ @http.verify_callback = verifyCallbackProc
379
+ @http.cert = OpenSSL::X509::Certificate.new(cert)
380
+ @http.key = OpenSSL::PKey::RSA.new(key)
381
+ rescue OpenSSL::PKey::RSAError, OpenSSL::X509::CertificateError => e
382
+ @logger.error "##### Error loading SSL client cert or key: #{e.message} :: backtrace #{e.backtrace}"
383
+ raise e
384
+ end
385
+ end
327
386
  end
328
387
  # open connection
329
388
  @http.start
@@ -359,14 +418,21 @@ them.
359
418
  current_params = @params.merge(request_params)
360
419
  exception = get_param(:exception, current_params) || RuntimeError
361
420
 
421
+ # Re-establish the connection if any of auth params has changed
422
+ same_auth_params_as_before = SECURITY_PARAMS.select do |param|
423
+ request_params[param] != get_param(param)
424
+ end.empty?
425
+
362
426
  # We save the offset here so that if we need to retry, we can return the file pointer to its initial position
363
427
  mypos = get_fileptr_offset(current_params)
364
428
  loop do
429
+
365
430
  current_params[:protocol] ||= (current_params[:port] == 443 ? 'https' : 'http')
366
431
  # (re)open connection to server if none exists or params has changed
367
- same_server_as_before = @server == current_params[:server] &&
368
- @port == current_params[:port] &&
369
- @protocol == current_params[:protocol]
432
+ same_server_as_before = @server == current_params[:server] &&
433
+ @port == current_params[:port] &&
434
+ @protocol == current_params[:protocol] &&
435
+ same_auth_params_as_before
370
436
 
371
437
  # if we are inside a delay between retries: no requests this time!
372
438
  # (skip this step if the endpoint has changed)
@@ -389,6 +455,7 @@ them.
389
455
  unless @http &&
390
456
  @http.started? &&
391
457
  same_server_as_before
458
+ same_auth_params_as_before = true
392
459
  start(current_params)
393
460
  end
394
461
 
@@ -414,8 +481,9 @@ them.
414
481
 
415
482
  # EOFError means the server closed the connection on us.
416
483
  rescue EOFError => e
484
+ finish(e.message)
485
+
417
486
  @logger.debug("#{err_header} server #{@server} closed connection")
418
- @http = nil
419
487
 
420
488
  # if we have waited long enough - raise an exception...
421
489
  if raise_on_eof_exception?
@@ -427,31 +495,35 @@ them.
427
495
  # We will be retrying the request, so reset the file pointer
428
496
  reset_fileptr_offset(request, mypos)
429
497
  end
430
- rescue Exception => e # See comment at bottom for the list of errors seen...
431
- @http = nil
432
- timeout_exception = e.is_a?(Errno::ETIMEDOUT) || e.is_a?(Timeout::Error)
433
- # Omit retries if it was explicitly requested
434
- if current_params[:raise_on_timeout] && timeout_exception
498
+ rescue ArgumentError => e
499
+ finish(e.message)
500
+
501
+ if e.message.include?('wrong number of arguments (5 for 4)')
502
+ # seems our net_fix patch was overriden...
503
+ raise exception.new('incompatible Net::HTTP monkey-patch')
504
+ else
505
+ raise e
506
+ end
507
+
508
+ rescue Timeout::Error, SocketError, SystemCallError, Interrupt => e # See comment at bottom for the list of errors seen...
509
+ finish(e.message)
510
+ if e.is_a?(Errno::ETIMEDOUT) || e.is_a?(Timeout::Error)
511
+ # Omit retries if it was explicitly requested
435
512
  # #6481:
436
513
  # ... When creating a resource in EC2 (instance, volume, snapshot, etc) it is undetermined what happened if the call times out.
437
514
  # The resource may or may not have been created in EC2. Retrying the call may cause multiple resources to be created...
438
- raise e
439
- end
440
- # if ctrl+c is pressed - we have to reraise exception to terminate proggy
441
- if e.is_a?(Interrupt) && !timeout_exception
515
+ raise exception.new("#{e.class.name}: #{e.message}") if current_params[:raise_on_timeout]
516
+ elsif e.is_a?(Interrupt)
517
+ # if ctrl+c is pressed - we have to reraise exception to terminate proggy
442
518
  @logger.debug( "#{err_header} request to server #{@server} interrupted by ctrl-c")
443
- raise
444
- elsif e.is_a?(ArgumentError) && e.message.include?('wrong number of arguments (5 for 4)')
445
- # seems our net_fix patch was overriden...
446
- raise exception.new('incompatible Net::HTTP monkey-patch')
519
+ raise e
447
520
  end
448
521
  # oops - we got a banana: log it
449
- error_add(e.message)
522
+ error_add(e)
450
523
  @logger.warn("#{err_header} request failure count: #{error_count}, exception: #{e.inspect}")
451
524
 
452
525
  # We will be retrying the request, so reset the file pointer
453
526
  reset_fileptr_offset(request, mypos)
454
-
455
527
  end
456
528
  end
457
529
  end
@@ -462,6 +534,8 @@ them.
462
534
  @logger.info("Closing #{@http.use_ssl? ? 'HTTPS' : 'HTTP'} connection to #{@http.address}:#{@http.port}#{reason}")
463
535
  @http.finish
464
536
  end
537
+ ensure
538
+ @http = nil
465
539
  end
466
540
 
467
541
  # Errors received during testing:
@@ -22,7 +22,7 @@
22
22
  #++
23
23
 
24
24
  require 'rubygems'
25
- require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'version'))
25
+ require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'base', 'version'))
26
26
 
27
27
  Gem::Specification.new do |spec|
28
28
  spec.name = 'right_http_connection'
Binary file
@@ -0,0 +1,17 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICxjCCAa6gAwIBAgIJAJYV+DprCQ1CMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
3
+ BAMTCE15VGVzdENBMB4XDTExMDkxMzIyNTMxMVoXDTEyMDkxMjIyNTMxMVowEzER
4
+ MA8GA1UEAxMITXlUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
5
+ AQCwmvBNrd7t/Z7ZVo1YfimpGgerOn1vXZY+OGJtqo+pN11Ei7dhVQfWBd2dAkYH
6
+ B8NlPr5QyxmIT88JIRKEzk7ZZ+nRdfyoocg63FeLn+b6OeR5hwyK38aMRbhqY1Gq
7
+ aIKMYyEpv0YNbuwoomv5Atl8mwvuUFr2XKndyzsrP1TrTCHH4lA5P0UUzIjVyyz9
8
+ F4YAjGLjjoVO5R02LmZ/h/LqT6bJQ+cu/2JeIWGVnjKoFvyWHd0TOaOGDHlQc5h8
9
+ RxgdOFrjsZGpQ5sKlhcI+9p0LOXqVfoC2J2ZWtAjFo0d54E/OarnBPFB6VNtoSmj
10
+ l0z+OLGMKuDGaLflXNE0STVdAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0P
11
+ BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAAbVkvPzS59uhX6Ox1ZT92cJXW8yjP
12
+ IyXrZjcWlaKQSKcn8v5RpebtVA+pL6mCActBE8fMac5ixlwTTnF5LHb9v80XuXMe
13
+ MXooQZBliyim5lVCp9gjKZYXEeVDphsuwDr5M4qO7tdZTB1ezCULObVF1N7qMwpO
14
+ yWI6zifRtLsgWmnRyaeVyv2uNRYoAEsAd2Dj4oJjvuyc9U5QUhtsXwD3jvSPsdi6
15
+ Mbr5tVIcZSpT4W9PSiZw2ZUZXIEbxX+w+FsuehhvoFJCi05R1ashCPxQA13bOJK0
16
+ BmbHqeLDzJCK0+kQs8CRIGWGTGng84AyJ5MygGzd0WN9jtZslWTPDtbz
17
+ -----END CERTIFICATE-----
@@ -0,0 +1,18 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC9TCCAd2gAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
3
+ c3RDQTAeFw0xMTA5MTMyMzAxMDVaFw0xMjA5MTIyMzAxMDVaMDgxJTAjBgNVBAMM
4
+ HE1haGVuZHJhLUt1dGFyZXMtTWFjQm9vay1Qcm8xDzANBgNVBAoMBmNsaWVudDCC
5
+ ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcEhrVTKKgWVqTUBCGBWJlD
6
+ u7RIk7kPcGAARy7Ctx+4VReEYlgVRqECzt4itglNdrQkUVCHXE0rwJMOozE8Hsgh
7
+ rAOQvxzPJhG3hPUJf/VkfB+Dn0xRsPvrE90HpRlSqdT8X6iuryPEmp5RyMaY122P
8
+ r/+Xs+lHhRlKQPdRpYXHlOwWX/U56Wy7jjGU9lONBEIEV8tD5ExzkCG23nbCvrFr
9
+ /2c4VjrAwXR2RyYfSDRyc/obky49ydKZ8/HKbS3VdJYAWBI4Wnj2hayCcZggEFB0
10
+ zg/IDXpOjnr6zV5UEfdaMIH4/K44ISX7xmZWGmQ3464NTmykj5xUMmfy3rVNREsC
11
+ AwEAAaMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
12
+ BQUHAwIwDQYJKoZIhvcNAQEFBQADggEBACmNyOoCvNsz8N3LN47VZK7aev54tjtd
13
+ zJilLgAxEGBeaIvHX9LDkgi3sQAvHMHc3VIq4BoEd9TNtyxIrUdc2EG1TCJvHINP
14
+ 7YoHtbajvT3bhVLlnWjB7jHp9jNfZtHL7aEDp+5eqPT6wzaVeiu1nABs7gudCQq1
15
+ CJw0Mfz1U3mG0sTb5JlRt7toce9dW0R6jfYTmTj6Yzu3kcgYjQKy2k2BCInLOIhz
16
+ 6tyOH51mCGAy1zgcWMvuyKYCeJQxRd46GrR2peyE2wYY6SfSlrK16pjaz48S3uhI
17
+ 01jd+HA1LARcImMhkMa/QFTo4uI7lx9Q+Y06Ny+rMuTNSnBSIgCUPQk=
18
+ -----END CERTIFICATE-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpAIBAAKCAQEAxwSGtVMoqBZWpNQEIYFYmUO7tEiTuQ9wYABHLsK3H7hVF4Ri
3
+ WBVGoQLO3iK2CU12tCRRUIdcTSvAkw6jMTweyCGsA5C/HM8mEbeE9Ql/9WR8H4Of
4
+ TFGw++sT3QelGVKp1PxfqK6vI8SanlHIxpjXbY+v/5ez6UeFGUpA91GlhceU7BZf
5
+ 9TnpbLuOMZT2U40EQgRXy0PkTHOQIbbedsK+sWv/ZzhWOsDBdHZHJh9INHJz+huT
6
+ Lj3J0pnz8cptLdV0lgBYEjhaePaFrIJxmCAQUHTOD8gNek6OevrNXlQR91owgfj8
7
+ rjghJfvGZlYaZDfjrg1ObKSPnFQyZ/LetU1ESwIDAQABAoIBAB23pU3KHxYKT+HI
8
+ 7tz57XrlTE/9TmGh1ovfPsHSvXl1Eu+yCuVQN/2u56jv0fLNqF351lKKA9RaJiVP
9
+ WDrv2UDVFlRp9r+chvi6SJY2Vu8TlB04kD7bK+xSC+NDUvnXCBkPnlEX1HsozlW5
10
+ rJtLE0/+1q75vhmlXlCKb+z+OhMhmFnaWTf/xLNbkItO5tOf+mv/CoqBUSEk+i9t
11
+ O6Zjzh02jbpW7xH3jJ/UexKMYOuqxoOMfC/MI6q3Qcu2OeZgl8cEIi94sjafq9ob
12
+ WcFTrZY+YG5b1SE8ILg69Fkqve5d2Mn1sN8mYZxLeM0C/ATNghM5uSWhdze06bNu
13
+ fpcgvOECgYEA/L+J/xVgUySUByELEBosY8q0HYG5Msq+GT7L5GMIoEmEp4j6MPRu
14
+ kF/DihxefcvDyVRLhJh7o/kwR7Vwe4wP9145e3MOe9b7IH6pEwV0nAsBO6ldVToX
15
+ gvrHOIoySNt/XtRurrbtZ08OtUDCLIRQATTnY9ieh8sxTyl1G9GehoMCgYEAyZQE
16
+ r4ByBzXjTiuaODH6tPndbKFxRo2iis9CyxqYXAMDkjvF4NEpQyW5ucRxpRqTt51P
17
+ kR13jdadnOF4t82M0qqEH3G6H4biKisY1jXRNH7mPSbyPbC4vxrQhnAEF3RiqbXz
18
+ f2LUC4uOtLzW7HeyjEiZy2mg7UKdOfsmmJ//oJkCgYEAxZF/8GqoQjW8lJoKyMp8
19
+ 2oDQLKSDvSVoVdmVjfCwBIOTc1aKpAveBXMmKealIlZOtCj1Yy/CrlmSmOtGgvzo
20
+ WihIbKxyrPFOmocH6PuBvJyJmTZ5464mRNd9NUApsHQL63fJET+i8feFer+lSSEg
21
+ XOEa4xyoR2PZJpU0mstPzLsCgYBvcS3F+TURV3F7Xg+80aTROPJ5hCej4dni9ALx
22
+ Vpq1A9WNmw4i5H/zZ3/ue/R4WuEfuhCrIade+y/X869RrooUTcENwUos891Fgt4Q
23
+ T2CBrUaMuGNkR7dbr+9o47TfYrDJMpaT7odceqNCuMP5p5NGizy7gII/qXxS+c60
24
+ woAIwQKBgQCiIfXZtAgYTPL23CQrxIMFwnlO0TiOe0ha0et7hjCh/CStG7NET7KK
25
+ U1L1kfyl1YDgoJbLXTsG2WwGZRnK1oyEEFj2iY5EvwoMPr0Sv8/CiOIyEfC62s3V
26
+ MoHemunnFhAj+JAy2HTKV0VYiNNNAxz3CBG8yMLK7YAMgPw1/HQQLQ==
27
+ -----END RSA PRIVATE KEY-----
@@ -23,7 +23,7 @@
23
23
 
24
24
  require 'webrick'
25
25
 
26
- ssl_cert, ssl_key = ARGV[0], ARGV[1]
26
+ ssl_cert, ssl_key, ca_cert = ARGV[0], ARGV[1], ARGV[2]
27
27
 
28
28
  # Monkey patch bad User-Agent parsing
29
29
  module WEBrick::AccessLog
@@ -52,7 +52,7 @@ module WEBrick::AccessLog
52
52
  end
53
53
  end
54
54
 
55
- logger = WEBrick::Log.new($stderr, WEBrick::Log::WARN)
55
+ logger = WEBrick::Log.new($stderr, WEBrick::Log::WARN)#WEBrick::Log::DEBUG
56
56
  config = {}
57
57
  config[:Port] = 7890
58
58
  config[:Logger] = logger
@@ -60,10 +60,19 @@ config[:AccessLog] = [[$stdout, WEBrick::AccessLog::COMBINED_LOG_FORMAT]]
60
60
  unless ssl_cert.nil? || ssl_key.nil?
61
61
  require 'webrick/https'
62
62
  config[:SSLEnable] = true
63
- config[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_NONE
63
+ # http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html#
64
+ # SSL_VERIFY_FAIL_IF_NO_PEER_CERT
65
+ # => Server mode: if the client did not return a certificate, the TLS/SSL handshake is immediately terminated with a 'handshake failure' alert.
66
+ # => This flag must be used together with SSL_VERIFY_PEER.
67
+ config[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER
68
+ config[:SSLVerifyClient] |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT if ca_cert
64
69
  config[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.open(ssl_key).read)
65
70
  config[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.open(ssl_cert).read)
66
- config[:SSLCertName] = [["CN", "Graham Hughes"]]
71
+ # KHRVI: option config[:SSLCertName] does make sense only when config[:SSLCertificate] isn't specified
72
+ # see: webrick/ssl.rb method :setup_ssl_context
73
+ # config[:SSLCertName] = [["CN", "Graham Hughes"]]
74
+ config[:SSLVerifyDepth] = 9
75
+ config[:SSLCACertificateFile] = ca_cert if ca_cert
67
76
  end
68
77
  $stdout.sync = true
69
78
  server = WEBrick::HTTPServer.new(config)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_http_connection
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
5
- prerelease: false
4
+ hash: 7
5
+ prerelease:
6
6
  segments:
7
7
  - 1
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 1.3.0
10
+ version: 1.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - RightScale, Inc.
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-25 00:00:00 -07:00
19
- default_executable:
18
+ date: 2013-06-13 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: rake
@@ -115,10 +114,10 @@ files:
115
114
  - Manifest.txt
116
115
  - README.txt
117
116
  - Rakefile
118
- - lib/net_fix.rb
117
+ - lib/base/net_fix.rb
118
+ - lib/base/support.rb
119
+ - lib/base/version.rb
119
120
  - lib/right_http_connection.rb
120
- - lib/support.rb
121
- - lib/version.rb
122
121
  - right_http_connection.gemspec
123
122
  - spec/bad.ca
124
123
  - spec/ca/Rakefile
@@ -128,12 +127,15 @@ files:
128
127
  - spec/ca/demoCA/serial
129
128
  - spec/ca/passphrase.txt
130
129
  - spec/ca/server.csr
130
+ - spec/client/cacert.cer
131
+ - spec/client/cacert.pem
132
+ - spec/client/cert.pem
133
+ - spec/client/key.pem
131
134
  - spec/good.ca
132
135
  - spec/proxy_server.rb
133
136
  - spec/really_dumb_webserver.rb
134
137
  - spec/server.crt
135
138
  - spec/server.key
136
- has_rdoc: true
137
139
  homepage: http://rightscale.rubyforge.org/
138
140
  licenses: []
139
141
 
@@ -173,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
175
  requirements: []
174
176
 
175
177
  rubyforge_project: rightscale
176
- rubygems_version: 1.3.7
178
+ rubygems_version: 1.8.5
177
179
  signing_key:
178
180
  specification_version: 3
179
181
  summary: RightScale's robust HTTP/S connection module