aws-sdk-s3 1.75.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.
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