aws-sdk-s3 1.75.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-s3.rb +73 -0
  3. data/lib/aws-sdk-s3/bucket.rb +861 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +277 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +262 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +264 -0
  7. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +283 -0
  8. data/lib/aws-sdk-s3/bucket_logging.rb +251 -0
  9. data/lib/aws-sdk-s3/bucket_notification.rb +293 -0
  10. data/lib/aws-sdk-s3/bucket_policy.rb +242 -0
  11. data/lib/aws-sdk-s3/bucket_region_cache.rb +81 -0
  12. data/lib/aws-sdk-s3/bucket_request_payment.rb +236 -0
  13. data/lib/aws-sdk-s3/bucket_tagging.rb +251 -0
  14. data/lib/aws-sdk-s3/bucket_versioning.rb +312 -0
  15. data/lib/aws-sdk-s3/bucket_website.rb +292 -0
  16. data/lib/aws-sdk-s3/client.rb +11818 -0
  17. data/lib/aws-sdk-s3/client_api.rb +3014 -0
  18. data/lib/aws-sdk-s3/customizations.rb +34 -0
  19. data/lib/aws-sdk-s3/customizations/bucket.rb +162 -0
  20. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +44 -0
  21. data/lib/aws-sdk-s3/customizations/object.rb +389 -0
  22. data/lib/aws-sdk-s3/customizations/object_summary.rb +85 -0
  23. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +13 -0
  24. data/lib/aws-sdk-s3/encryption.rb +21 -0
  25. data/lib/aws-sdk-s3/encryption/client.rb +375 -0
  26. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +190 -0
  27. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +65 -0
  28. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +40 -0
  29. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +61 -0
  30. data/lib/aws-sdk-s3/encryption/errors.rb +15 -0
  31. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +58 -0
  32. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +36 -0
  33. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +71 -0
  34. data/lib/aws-sdk-s3/encryption/key_provider.rb +31 -0
  35. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +75 -0
  36. data/lib/aws-sdk-s3/encryption/materials.rb +60 -0
  37. data/lib/aws-sdk-s3/encryption/utils.rb +81 -0
  38. data/lib/aws-sdk-s3/encryptionV2/client.rb +388 -0
  39. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +198 -0
  40. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +103 -0
  41. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
  42. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +66 -0
  43. data/lib/aws-sdk-s3/encryptionV2/errors.rb +13 -0
  44. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +56 -0
  45. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +35 -0
  46. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +71 -0
  47. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +29 -0
  48. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +99 -0
  49. data/lib/aws-sdk-s3/encryptionV2/materials.rb +58 -0
  50. data/lib/aws-sdk-s3/encryptionV2/utils.rb +116 -0
  51. data/lib/aws-sdk-s3/encryption_v2.rb +20 -0
  52. data/lib/aws-sdk-s3/errors.rb +115 -0
  53. data/lib/aws-sdk-s3/event_streams.rb +69 -0
  54. data/lib/aws-sdk-s3/file_downloader.rb +142 -0
  55. data/lib/aws-sdk-s3/file_part.rb +78 -0
  56. data/lib/aws-sdk-s3/file_uploader.rb +70 -0
  57. data/lib/aws-sdk-s3/legacy_signer.rb +189 -0
  58. data/lib/aws-sdk-s3/multipart_file_uploader.rb +227 -0
  59. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +173 -0
  60. data/lib/aws-sdk-s3/multipart_upload.rb +401 -0
  61. data/lib/aws-sdk-s3/multipart_upload_error.rb +18 -0
  62. data/lib/aws-sdk-s3/multipart_upload_part.rb +423 -0
  63. data/lib/aws-sdk-s3/object.rb +1422 -0
  64. data/lib/aws-sdk-s3/object_acl.rb +333 -0
  65. data/lib/aws-sdk-s3/object_copier.rb +101 -0
  66. data/lib/aws-sdk-s3/object_multipart_copier.rb +182 -0
  67. data/lib/aws-sdk-s3/object_summary.rb +1181 -0
  68. data/lib/aws-sdk-s3/object_version.rb +550 -0
  69. data/lib/aws-sdk-s3/plugins/accelerate.rb +87 -0
  70. data/lib/aws-sdk-s3/plugins/bucket_arn.rb +212 -0
  71. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +91 -0
  72. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +45 -0
  73. data/lib/aws-sdk-s3/plugins/dualstack.rb +74 -0
  74. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +28 -0
  75. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +25 -0
  76. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -0
  77. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +62 -0
  78. data/lib/aws-sdk-s3/plugins/location_constraint.rb +35 -0
  79. data/lib/aws-sdk-s3/plugins/md5s.rb +84 -0
  80. data/lib/aws-sdk-s3/plugins/redirects.rb +45 -0
  81. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +30 -0
  82. data/lib/aws-sdk-s3/plugins/s3_signer.rb +222 -0
  83. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +70 -0
  84. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
  85. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +97 -0
  86. data/lib/aws-sdk-s3/presigned_post.rb +686 -0
  87. data/lib/aws-sdk-s3/presigner.rb +253 -0
  88. data/lib/aws-sdk-s3/resource.rb +117 -0
  89. data/lib/aws-sdk-s3/types.rb +13154 -0
  90. data/lib/aws-sdk-s3/waiters.rb +243 -0
  91. metadata +184 -0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING ABOUT GENERATED CODE
4
+ #
5
+ # This file is generated. See the contributing guide for more information:
6
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
7
+ #
8
+ # WARNING ABOUT GENERATED CODE
9
+
10
+ module Aws::S3
11
+ module EventStreams
12
+ class SelectObjectContentEventStream
13
+
14
+ def initialize
15
+ @event_emitter = Aws::EventEmitter.new
16
+ end
17
+
18
+ def on_records_event(&block)
19
+ @event_emitter.on(:records, block) if block_given?
20
+ end
21
+
22
+ def on_stats_event(&block)
23
+ @event_emitter.on(:stats, block) if block_given?
24
+ end
25
+
26
+ def on_progress_event(&block)
27
+ @event_emitter.on(:progress, block) if block_given?
28
+ end
29
+
30
+ def on_cont_event(&block)
31
+ @event_emitter.on(:cont, block) if block_given?
32
+ end
33
+
34
+ def on_end_event(&block)
35
+ @event_emitter.on(:end, block) if block_given?
36
+ end
37
+
38
+ def on_error_event(&block)
39
+ @event_emitter.on(:error, block) if block_given?
40
+ end
41
+
42
+ def on_initial_response_event(&block)
43
+ @event_emitter.on(:initial_response, block) if block_given?
44
+ end
45
+
46
+ def on_unknown_event(&block)
47
+ @event_emitter.on(:unknown_event, block) if block_given?
48
+ end
49
+
50
+ def on_event(&block)
51
+ on_records_event(&block)
52
+ on_stats_event(&block)
53
+ on_progress_event(&block)
54
+ on_cont_event(&block)
55
+ on_end_event(&block)
56
+ on_error_event(&block)
57
+ on_initial_response_event(&block)
58
+ on_unknown_event(&block)
59
+ end
60
+
61
+ # @api private
62
+ # @return Aws::EventEmitter
63
+ attr_reader :event_emitter
64
+
65
+ end
66
+
67
+ end
68
+ end
69
+
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'thread'
5
+ require 'set'
6
+ require 'tmpdir'
7
+
8
+ module Aws
9
+ module S3
10
+ # @api private
11
+ class FileDownloader
12
+
13
+ MIN_CHUNK_SIZE = 5 * 1024 * 1024
14
+ MAX_PARTS = 10_000
15
+ THREAD_COUNT = 10
16
+
17
+ def initialize(options = {})
18
+ @client = options[:client] || Client.new
19
+ end
20
+
21
+ # @return [Client]
22
+ attr_reader :client
23
+
24
+ def download(destination, options = {})
25
+ @path = destination
26
+ @mode = options[:mode] || 'auto'
27
+ @thread_count = options[:thread_count] || THREAD_COUNT
28
+ @chunk_size = options[:chunk_size]
29
+ @params = {
30
+ bucket: options[:bucket],
31
+ key: options[:key],
32
+ }
33
+ @params[:version_id] = options[:version_id] if options[:version_id]
34
+
35
+ case @mode
36
+ when 'auto' then multipart_download
37
+ when 'single_request' then single_request
38
+ when 'get_range'
39
+ if @chunk_size
40
+ resp = @client.head_object(@params)
41
+ multithreaded_get_by_ranges(construct_chunks(resp.content_length))
42
+ else
43
+ msg = 'In :get_range mode, :chunk_size must be provided'
44
+ raise ArgumentError, msg
45
+ end
46
+ else
47
+ msg = "Invalid mode #{@mode} provided, "\
48
+ 'mode should be :single_request, :get_range or :auto'
49
+ raise ArgumentError, msg
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def multipart_download
56
+ resp = @client.head_object(@params.merge(part_number: 1))
57
+ count = resp.parts_count
58
+ if count.nil? || count <= 1
59
+ resp.content_length < MIN_CHUNK_SIZE ?
60
+ single_request :
61
+ multithreaded_get_by_ranges(construct_chunks(resp.content_length))
62
+ else
63
+ # partNumber is an option
64
+ resp = @client.head_object(@params)
65
+ resp.content_length < MIN_CHUNK_SIZE ?
66
+ single_request :
67
+ compute_mode(resp.content_length, count)
68
+ end
69
+ end
70
+
71
+ def compute_mode(file_size, count)
72
+ chunk_size = compute_chunk(file_size)
73
+ part_size = (file_size.to_f / count.to_f).ceil
74
+ if chunk_size < part_size
75
+ multithreaded_get_by_ranges(construct_chunks(file_size))
76
+ else
77
+ multithreaded_get_by_parts(count)
78
+ end
79
+ end
80
+
81
+ def construct_chunks(file_size)
82
+ offset = 0
83
+ default_chunk_size = compute_chunk(file_size)
84
+ chunks = []
85
+ while offset <= file_size
86
+ progress = offset + default_chunk_size
87
+ chunks << "bytes=#{offset}-#{progress < file_size ? progress : file_size}"
88
+ offset = progress + 1
89
+ end
90
+ chunks
91
+ end
92
+
93
+ def compute_chunk(file_size)
94
+ if @chunk_size && @chunk_size > file_size
95
+ raise ArgumentError, ":chunk_size shouldn't exceed total file size."
96
+ else
97
+ @chunk_size || [(file_size.to_f / MAX_PARTS).ceil, MIN_CHUNK_SIZE].max.to_i
98
+ end
99
+ end
100
+
101
+ def batches(chunks, mode)
102
+ chunks = (1..chunks) if mode.eql? 'part_number'
103
+ chunks.each_slice(@thread_count).to_a
104
+ end
105
+
106
+ def multithreaded_get_by_ranges(chunks)
107
+ thread_batches(chunks, 'range')
108
+ end
109
+
110
+ def multithreaded_get_by_parts(parts)
111
+ thread_batches(parts, 'part_number')
112
+ end
113
+
114
+ def thread_batches(chunks, param)
115
+ batches(chunks, param).each do |batch|
116
+ threads = []
117
+ batch.each do |chunk|
118
+ threads << Thread.new do
119
+ resp = @client.get_object(
120
+ @params.merge(param.to_sym => chunk)
121
+ )
122
+ write(resp)
123
+ end
124
+ end
125
+ threads.each(&:join)
126
+ end
127
+ end
128
+
129
+ def write(resp)
130
+ range, _ = resp.content_range.split(' ').last.split('/')
131
+ head, _ = range.split('-').map {|s| s.to_i}
132
+ IO.write(@path, resp.body.read, head)
133
+ end
134
+
135
+ def single_request
136
+ @client.get_object(
137
+ @params.merge(response_target: @path)
138
+ )
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+
6
+ # A utility class that provides an IO-like interface to a portion of a file
7
+ # on disk.
8
+ # @api private
9
+ class FilePart
10
+
11
+ # @option options [required, String, Pathname, File, Tempfile] :source
12
+ # The file to upload.
13
+ #
14
+ # @option options [required, Integer] :offset The file part will read
15
+ # starting at this byte offset.
16
+ #
17
+ # @option options [required, Integer] :size The maximum number of bytes to
18
+ # read from the `:offset`.
19
+ def initialize(options = {})
20
+ @source = options[:source]
21
+ @first_byte = options[:offset]
22
+ @last_byte = @first_byte + options[:size]
23
+ @size = options[:size]
24
+ @file = nil
25
+ end
26
+
27
+ # @return [String, Pathname, File, Tempfile]
28
+ attr_reader :source
29
+
30
+ # @return [Integer]
31
+ attr_reader :first_byte
32
+
33
+ # @return [Integer]
34
+ attr_reader :last_byte
35
+
36
+ # @return [Integer]
37
+ attr_reader :size
38
+
39
+ def read(bytes = nil, output_buffer = nil)
40
+ open_file unless @file
41
+ read_from_file(bytes, output_buffer)
42
+ end
43
+
44
+ def rewind
45
+ if @file
46
+ @file.seek(@first_byte)
47
+ @position = @first_byte
48
+ end
49
+ 0
50
+ end
51
+
52
+ def close
53
+ @file.close if @file
54
+ end
55
+
56
+ private
57
+
58
+ def open_file
59
+ @file = File.open(@source, 'rb')
60
+ rewind
61
+ end
62
+
63
+ def read_from_file(bytes, output_buffer)
64
+ length = [remaining_bytes, *bytes].min
65
+ data = @file.read(length, output_buffer)
66
+
67
+ @position += data ? data.bytesize : 0
68
+
69
+ data.to_s unless bytes && (data.nil? || data.empty?)
70
+ end
71
+
72
+ def remaining_bytes
73
+ @last_byte - @position
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module Aws
6
+ module S3
7
+ # @api private
8
+ class FileUploader
9
+
10
+ FIFTEEN_MEGABYTES = 15 * 1024 * 1024
11
+
12
+ # @param [Hash] options
13
+ # @option options [Client] :client
14
+ # @option options [Integer] :multipart_threshold (15728640)
15
+ def initialize(options = {})
16
+ @options = options
17
+ @client = options[:client] || Client.new
18
+ @multipart_threshold = options[:multipart_threshold] ||
19
+ FIFTEEN_MEGABYTES
20
+ end
21
+
22
+ # @return [Client]
23
+ attr_reader :client
24
+
25
+ # @return [Integer] Files larger than this in bytes are uploaded
26
+ # using a {MultipartFileUploader}.
27
+ attr_reader :multipart_threshold
28
+
29
+ # @param [String, Pathname, File, Tempfile] source The file to upload.
30
+ # @option options [required, String] :bucket The bucket to upload to.
31
+ # @option options [required, String] :key The key for the object.
32
+ # @option options [Proc] :progress_callback
33
+ # A Proc that will be called when each chunk of the upload is sent.
34
+ # It will be invoked with [bytes_read], [total_sizes]
35
+ # @return [void]
36
+ def upload(source, options = {})
37
+ if File.size(source) >= multipart_threshold
38
+ MultipartFileUploader.new(@options).upload(source, options)
39
+ else
40
+ put_object(source, options)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def open_file(source)
47
+ if String === source || Pathname === source
48
+ File.open(source, 'rb') { |file| yield(file) }
49
+ else
50
+ yield(source)
51
+ end
52
+ end
53
+
54
+ def put_object(source, options)
55
+ if (callback = options.delete(:progress_callback))
56
+ options[:on_chunk_sent] = single_part_progress(callback)
57
+ end
58
+ open_file(source) do |file|
59
+ @client.put_object(options.merge(body: file))
60
+ end
61
+ end
62
+
63
+ def single_part_progress(progress_callback)
64
+ proc do |_chunk, bytes_read, total_size|
65
+ progress_callback.call([bytes_read], [total_size])
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'time'
5
+ require 'openssl'
6
+ require 'cgi'
7
+ require 'webrick/httputils'
8
+ require 'aws-sdk-core/query'
9
+
10
+ module Aws
11
+ module S3
12
+ # @api private
13
+ class LegacySigner
14
+
15
+ SIGNED_QUERYSTRING_PARAMS = Set.new(%w(
16
+
17
+ acl delete cors lifecycle location logging notification partNumber
18
+ policy requestPayment restore tagging torrent uploadId uploads
19
+ versionId versioning versions website replication requestPayment
20
+ accelerate
21
+
22
+ response-content-type response-content-language
23
+ response-expires response-cache-control
24
+ response-content-disposition response-content-encoding
25
+
26
+ ))
27
+
28
+ def self.sign(context)
29
+ new(
30
+ context.config.credentials,
31
+ context.params,
32
+ context.config.force_path_style
33
+ ).sign(context.http_request)
34
+ end
35
+
36
+ # @param [CredentialProvider] credentials
37
+ def initialize(credentials, params, force_path_style)
38
+ @credentials = credentials.credentials
39
+ @params = Query::ParamList.new
40
+ params.each_pair do |param_name, param_value|
41
+ @params.set(param_name, param_value)
42
+ end
43
+ @force_path_style = force_path_style
44
+ end
45
+
46
+ attr_reader :credentials, :params
47
+
48
+ def sign(request)
49
+ if token = credentials.session_token
50
+ request.headers["X-Amz-Security-Token"] = token
51
+ end
52
+ request.headers['Authorization'] = authorization(request)
53
+ end
54
+
55
+ def authorization(request)
56
+ "AWS #{credentials.access_key_id}:#{signature(request)}"
57
+ end
58
+
59
+ def signature(request)
60
+ string_to_sign = string_to_sign(request)
61
+ signature = digest(credentials.secret_access_key, string_to_sign)
62
+ uri_escape(signature)
63
+ end
64
+
65
+ def digest(secret, string_to_sign)
66
+ Base64.encode64(hmac(secret, string_to_sign)).strip
67
+ end
68
+
69
+ def hmac(key, value)
70
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), key, value)
71
+ end
72
+
73
+ # From the S3 developer guide:
74
+ #
75
+ # StringToSign =
76
+ # HTTP-Verb ` "\n" `
77
+ # content-md5 ` "\n" `
78
+ # content-type ` "\n" `
79
+ # date ` "\n" `
80
+ # CanonicalizedAmzHeaders + CanonicalizedResource;
81
+ #
82
+ def string_to_sign(request)
83
+ [
84
+ request.http_method,
85
+ request.headers.values_at('Content-Md5', 'Content-Type').join("\n"),
86
+ signing_string_date(request),
87
+ canonicalized_headers(request),
88
+ canonicalized_resource(request.endpoint),
89
+ ].flatten.compact.join("\n")
90
+ end
91
+
92
+ def signing_string_date(request)
93
+ # if a date is provided via x-amz-date then we should omit the
94
+ # Date header from the signing string (should appear as a blank line)
95
+ if request.headers.detect{|k,v| k.to_s =~ /^x-amz-date$/i }
96
+ ''
97
+ else
98
+ request.headers['Date'] = Time.now.httpdate
99
+ end
100
+ end
101
+
102
+ # CanonicalizedAmzHeaders
103
+ #
104
+ # See the developer guide for more information on how this element
105
+ # is generated.
106
+ #
107
+ def canonicalized_headers(request)
108
+ x_amz = request.headers.select{|k, v| k =~ /^x-amz-/i }
109
+ x_amz = x_amz.collect{|k, v| [k.downcase, v] }
110
+ x_amz = x_amz.sort_by{|k, v| k }
111
+ x_amz = x_amz.collect{|k, v| "#{k}:#{v.to_s.strip}" }.join("\n")
112
+ x_amz == '' ? nil : x_amz
113
+ end
114
+
115
+ # From the S3 developer guide
116
+ #
117
+ # CanonicalizedResource =
118
+ # [ "/" ` Bucket ] `
119
+ # <HTTP-Request-URI, protocol name up to the querystring> +
120
+ # [ sub-resource, if present. e.g. "?acl", "?location",
121
+ # "?logging", or "?torrent"];
122
+ #
123
+ # @api private
124
+ def canonicalized_resource(endpoint)
125
+
126
+ parts = []
127
+
128
+ # virtual hosted-style requests require the hostname to appear
129
+ # in the canonicalized resource prefixed by a forward slash.
130
+ if bucket = params[:bucket]
131
+ bucket = bucket.value
132
+ ssl = endpoint.scheme == 'https'
133
+ if Plugins::BucketDns.dns_compatible?(bucket, ssl) && !@force_path_style
134
+ parts << "/#{bucket}"
135
+ end
136
+ end
137
+
138
+ # append the path name (no querystring)
139
+ parts << endpoint.path
140
+
141
+ # lastly any sub resource querystring params need to be appened
142
+ # in lexigraphical ordered joined by '&' and prefixed by '?'
143
+ params = signed_querystring_params(endpoint)
144
+
145
+ unless params.empty?
146
+ parts << '?'
147
+ parts << params.sort.collect{|p| p.to_s }.join('&')
148
+ end
149
+
150
+ parts.join
151
+ end
152
+
153
+ def signed_querystring_params(endpoint)
154
+ endpoint.query.to_s.split('&').select do |p|
155
+ SIGNED_QUERYSTRING_PARAMS.include?(p.split('=')[0])
156
+ end.map { |p| CGI.unescape(p) }
157
+ end
158
+
159
+ def uri_escape(s)
160
+
161
+ #URI.escape(s)
162
+
163
+ # URI.escape is deprecated, replacing it with escape from webrick
164
+ # to squelch the massive number of warnings generated from Ruby.
165
+ # The following script was used to determine the differences
166
+ # between the various escape methods available. The webrick
167
+ # escape only had two differences and it is available in the
168
+ # standard lib.
169
+ #
170
+ # (0..255).each {|c|
171
+ # s = [c].pack("C")
172
+ # e = [
173
+ # CGI.escape(s),
174
+ # ERB::Util.url_encode(s),
175
+ # URI.encode_www_form_component(s),
176
+ # WEBrick::HTTPUtils.escape_form(s),
177
+ # WEBrick::HTTPUtils.escape(s),
178
+ # URI.escape(s),
179
+ # ]
180
+ # next if e.uniq.length == 1
181
+ # puts("%5s %5s %5s %5s %5s %5s %5s" % ([s.inspect] + e))
182
+ # }
183
+ #
184
+ WEBrick::HTTPUtils.escape(s).gsub('%5B', '[').gsub('%5D', ']')
185
+ end
186
+
187
+ end
188
+ end
189
+ end