aws-sdk-s3 1.0.0.rc1

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-s3.rb +66 -0
  3. data/lib/aws-sdk-s3/bucket.rb +595 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +168 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +146 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +164 -0
  7. data/lib/aws-sdk-s3/bucket_logging.rb +142 -0
  8. data/lib/aws-sdk-s3/bucket_notification.rb +187 -0
  9. data/lib/aws-sdk-s3/bucket_policy.rb +138 -0
  10. data/lib/aws-sdk-s3/bucket_region_cache.rb +79 -0
  11. data/lib/aws-sdk-s3/bucket_request_payment.rb +128 -0
  12. data/lib/aws-sdk-s3/bucket_tagging.rb +143 -0
  13. data/lib/aws-sdk-s3/bucket_versioning.rb +188 -0
  14. data/lib/aws-sdk-s3/bucket_website.rb +177 -0
  15. data/lib/aws-sdk-s3/client.rb +3171 -0
  16. data/lib/aws-sdk-s3/client_api.rb +1991 -0
  17. data/lib/aws-sdk-s3/customizations.rb +29 -0
  18. data/lib/aws-sdk-s3/customizations/bucket.rb +127 -0
  19. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +42 -0
  20. data/lib/aws-sdk-s3/customizations/object.rb +257 -0
  21. data/lib/aws-sdk-s3/customizations/object_summary.rb +65 -0
  22. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +11 -0
  23. data/lib/aws-sdk-s3/encryption.rb +19 -0
  24. data/lib/aws-sdk-s3/encryption/client.rb +369 -0
  25. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +178 -0
  26. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +63 -0
  27. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +38 -0
  28. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +50 -0
  29. data/lib/aws-sdk-s3/encryption/errors.rb +13 -0
  30. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +50 -0
  31. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +29 -0
  32. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +69 -0
  33. data/lib/aws-sdk-s3/encryption/key_provider.rb +29 -0
  34. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +71 -0
  35. data/lib/aws-sdk-s3/encryption/materials.rb +58 -0
  36. data/lib/aws-sdk-s3/encryption/utils.rb +79 -0
  37. data/lib/aws-sdk-s3/errors.rb +23 -0
  38. data/lib/aws-sdk-s3/file_part.rb +75 -0
  39. data/lib/aws-sdk-s3/file_uploader.rb +58 -0
  40. data/lib/aws-sdk-s3/legacy_signer.rb +186 -0
  41. data/lib/aws-sdk-s3/multipart_file_uploader.rb +187 -0
  42. data/lib/aws-sdk-s3/multipart_upload.rb +287 -0
  43. data/lib/aws-sdk-s3/multipart_upload_error.rb +16 -0
  44. data/lib/aws-sdk-s3/multipart_upload_part.rb +314 -0
  45. data/lib/aws-sdk-s3/object.rb +942 -0
  46. data/lib/aws-sdk-s3/object_acl.rb +214 -0
  47. data/lib/aws-sdk-s3/object_copier.rb +99 -0
  48. data/lib/aws-sdk-s3/object_multipart_copier.rb +179 -0
  49. data/lib/aws-sdk-s3/object_summary.rb +794 -0
  50. data/lib/aws-sdk-s3/object_version.rb +406 -0
  51. data/lib/aws-sdk-s3/plugins/accelerate.rb +92 -0
  52. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +89 -0
  53. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +23 -0
  54. data/lib/aws-sdk-s3/plugins/dualstack.rb +70 -0
  55. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +29 -0
  56. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +23 -0
  57. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +47 -0
  58. data/lib/aws-sdk-s3/plugins/location_constraint.rb +33 -0
  59. data/lib/aws-sdk-s3/plugins/md5s.rb +79 -0
  60. data/lib/aws-sdk-s3/plugins/redirects.rb +41 -0
  61. data/lib/aws-sdk-s3/plugins/s3_signer.rb +208 -0
  62. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +68 -0
  63. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +94 -0
  64. data/lib/aws-sdk-s3/presigned_post.rb +647 -0
  65. data/lib/aws-sdk-s3/presigner.rb +160 -0
  66. data/lib/aws-sdk-s3/resource.rb +96 -0
  67. data/lib/aws-sdk-s3/types.rb +5750 -0
  68. data/lib/aws-sdk-s3/waiters.rb +178 -0
  69. metadata +154 -0
@@ -0,0 +1,29 @@
1
+ module Aws
2
+ module S3
3
+ module Encryption
4
+
5
+ # This module defines the interface required for a {Client#key_provider}.
6
+ # A key provider is any object that:
7
+ #
8
+ # * Responds to {#encryption_materials} with an {Materials} object.
9
+ #
10
+ # * Responds to {#key_for}, receiving a JSON document String,
11
+ # returning an encryption key. The returned encryption key
12
+ # must be one of:
13
+ #
14
+ # * `OpenSSL::PKey::RSA` - for asymmetric encryption
15
+ # * `String` - 32, 24, or 16 bytes long, for symmetric encryption
16
+ #
17
+ module KeyProvider
18
+
19
+ # @return [Materials]
20
+ def encryption_materials; end
21
+
22
+ # @param [String<JSON>] materials_description
23
+ # @return [OpenSSL::PKey::RSA, String] encryption_key
24
+ def key_for(materials_description); end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ class KmsCipherProvider
8
+
9
+ def initialize(options = {})
10
+ @kms_key_id = options[:kms_key_id]
11
+ @kms_client = options[:kms_client]
12
+ end
13
+
14
+ # @return [Array<Hash,Cipher>] Creates an returns a new encryption
15
+ # envelope and encryption cipher.
16
+ def encryption_cipher
17
+ encryption_context = { "kms_cmk_id" => @kms_key_id }
18
+ key_data = @kms_client.generate_data_key(
19
+ key_id: @kms_key_id,
20
+ encryption_context: encryption_context,
21
+ key_spec: 'AES_256',
22
+ )
23
+ cipher = Utils.aes_encryption_cipher(:CBC)
24
+ cipher.key = key_data.plaintext
25
+ envelope = {
26
+ 'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
27
+ 'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
28
+ 'x-amz-cek-alg' => 'AES/CBC/PKCS5Padding',
29
+ 'x-amz-wrap-alg' => 'kms',
30
+ 'x-amz-matdesc' => Json.dump(encryption_context)
31
+ }
32
+ [envelope, cipher]
33
+ end
34
+
35
+ # @return [Cipher] Given an encryption envelope, returns a
36
+ # decryption cipher.
37
+ def decryption_cipher(envelope)
38
+ encryption_context = Json.load(envelope['x-amz-matdesc'])
39
+ key = @kms_client.decrypt(
40
+ ciphertext_blob: decode64(envelope['x-amz-key-v2']),
41
+ encryption_context: encryption_context,
42
+ ).plaintext
43
+ iv = decode64(envelope['x-amz-iv'])
44
+ block_mode =
45
+ case envelope['x-amz-cek-alg']
46
+ when 'AES/CBC/PKCS5Padding'
47
+ :CBC
48
+ when 'AES/GCM/NoPadding'
49
+ :GCM
50
+ else
51
+ type = envelope['x-amz-cek-alg'].inspect
52
+ msg = "unsupported content encrypting key (cek) format: #{type}"
53
+ raise Errors::DecryptionError, msg
54
+ end
55
+ Utils.aes_decryption_cipher(block_mode, key, iv)
56
+ end
57
+
58
+ private
59
+
60
+ def encode64(str)
61
+ Base64.encode64(str).split("\n") * ""
62
+ end
63
+
64
+ def decode64(str)
65
+ Base64.decode64(str)
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,58 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ class Materials
7
+
8
+ # @option options [required, OpenSSL::PKey::RSA, String] :key
9
+ # The master key to use for encrypting/decrypting all objects.
10
+ #
11
+ # @option options [String<JSON>] :description ('{}')
12
+ # The encryption materials description. This is must be
13
+ # a JSON document string.
14
+ #
15
+ def initialize(options = {})
16
+ @key = validate_key(options[:key])
17
+ @description = validate_desc(options[:description])
18
+ end
19
+
20
+ # @return [OpenSSL::PKey::RSA, String]
21
+ attr_reader :key
22
+
23
+ # @return [String<JSON>]
24
+ attr_reader :description
25
+
26
+ private
27
+
28
+ def validate_key(key)
29
+ case key
30
+ when OpenSSL::PKey::RSA then key
31
+ when String
32
+ if [32, 24, 16].include?(key.bytesize)
33
+ key
34
+ else
35
+ msg = "invalid key, symmetric key required to be 16, 24, or "
36
+ msg << "32 bytes in length, saw length " + key.bytesize.to_s
37
+ raise ArgumentError, msg
38
+ end
39
+ else
40
+ msg = "invalid encryption key, expected an OpenSSL::PKey::RSA key "
41
+ msg << "(for asymmetric encryption) or a String (for symmetric "
42
+ msg << "encryption)."
43
+ raise ArgumentError, msg
44
+ end
45
+ end
46
+
47
+ def validate_desc(description)
48
+ Json.load(description)
49
+ description
50
+ rescue Json::ParseError
51
+ msg = "expected description to be a valid JSON document string"
52
+ raise ArgumentError, msg
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,79 @@
1
+ require 'openssl'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ module Utils
8
+
9
+ UNSAFE_MSG = "unsafe encryption, data is longer than key length"
10
+
11
+ class << self
12
+
13
+ def encrypt(key, data)
14
+ case key
15
+ when OpenSSL::PKey::RSA # asymmetric encryption
16
+ warn(UNSAFE_MSG) if key.public_key.n.num_bits < cipher_size(data)
17
+ key.public_encrypt(data)
18
+ when String # symmetric encryption
19
+ warn(UNSAFE_MSG) if cipher_size(key) < cipher_size(data)
20
+ cipher = aes_encryption_cipher(:ECB, key)
21
+ cipher.update(data) + cipher.final
22
+ end
23
+ end
24
+
25
+ def decrypt(key, data)
26
+ begin
27
+ case key
28
+ when OpenSSL::PKey::RSA # asymmetric decryption
29
+ key.private_decrypt(data)
30
+ when String # symmetric Decryption
31
+ cipher = aes_cipher(:decrypt, :ECB, key, nil)
32
+ cipher.update(data) + cipher.final
33
+ end
34
+ rescue OpenSSL::Cipher::CipherError
35
+ msg = 'decryption failed, possible incorrect key'
36
+ raise Errors::DecryptionError, msg
37
+ end
38
+ end
39
+
40
+ # @param [String] block_mode "CBC" or "ECB"
41
+ # @param [OpenSSL::PKey::RSA, String, nil] key
42
+ # @param [String, nil] iv The initialization vector
43
+ def aes_encryption_cipher(block_mode, key = nil, iv = nil)
44
+ aes_cipher(:encrypt, block_mode, key, iv)
45
+ end
46
+
47
+ # @param [String] block_mode "CBC" or "ECB"
48
+ # @param [OpenSSL::PKey::RSA, String, nil] key
49
+ # @param [String, nil] iv The initialization vector
50
+ def aes_decryption_cipher(block_mode, key = nil, iv = nil)
51
+ aes_cipher(:decrypt, block_mode, key, iv)
52
+ end
53
+
54
+ # @param [String] mode "encrypt" or "decrypt"
55
+ # @param [String] block_mode "CBC" or "ECB"
56
+ # @param [OpenSSL::PKey::RSA, String, nil] key
57
+ # @param [String, nil] iv The initialization vector
58
+ def aes_cipher(mode, block_mode, key, iv)
59
+ cipher = key ?
60
+ OpenSSL::Cipher.new("aes-#{cipher_size(key)}-#{block_mode.downcase}") :
61
+ OpenSSL::Cipher.new("aes-256-#{block_mode.downcase}")
62
+ cipher.send(mode) # encrypt or decrypt
63
+ cipher.key = key if key
64
+ cipher.iv = iv if iv
65
+ cipher
66
+ end
67
+
68
+ # @param [String] key
69
+ # @return [Integer]
70
+ # @raise ArgumentError
71
+ def cipher_size(key)
72
+ key.bytesize * 8
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,23 @@
1
+ # WARNING ABOUT GENERATED CODE
2
+ #
3
+ # This file is generated. See the contributing for info on making contributions:
4
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
5
+ #
6
+ # WARNING ABOUT GENERATED CODE
7
+
8
+ module Aws
9
+ module S3
10
+ module Errors
11
+
12
+ extend Aws::Errors::DynamicErrors
13
+
14
+ # Raised when calling #load or #data on a resource class that can not be
15
+ # loaded. This can happen when:
16
+ #
17
+ # * A resource class has identifiers, but no data attributes.
18
+ # * Resource data is only available when making an API call that
19
+ # enumerates all resources of that type.
20
+ class ResourceNotLoadable < RuntimeError; end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,75 @@
1
+ module Aws
2
+ module S3
3
+
4
+ # A utility class that provides an IO-like interface to a portion of
5
+ # a file on disk.
6
+ # @api private
7
+ class FilePart
8
+
9
+ # @option options [required,String,Pathname,File,Tempfile] :source
10
+ # @option options [required,Integer] :offset The file part will read
11
+ # starting at this byte offset.
12
+ # @option options [required,Integer] :size The maximum number of bytes to
13
+ # read from the `:offset`.
14
+ def initialize(options = {})
15
+ @source = options[:source]
16
+ @first_byte = options[:offset]
17
+ @last_byte = @first_byte + options[:size]
18
+ @size = options[:size]
19
+ @file = nil
20
+ end
21
+
22
+ # @return [String,Pathname,File,Tempfile]
23
+ attr_reader :source
24
+
25
+ # @return [Integer]
26
+ attr_reader :first_byte
27
+
28
+ # @return [Integer]
29
+ attr_reader :last_byte
30
+
31
+ # @return [Integer]
32
+ attr_reader :size
33
+
34
+ def read(bytes = nil, output_buffer = nil)
35
+ open_file unless @file
36
+ read_from_file(bytes, output_buffer)
37
+ end
38
+
39
+ def rewind
40
+ if @file
41
+ @file.seek(@first_byte)
42
+ @position = @first_byte
43
+ end
44
+ 0
45
+ end
46
+
47
+ def close
48
+ @file.close if @file
49
+ end
50
+
51
+ private
52
+
53
+ def open_file
54
+ @file = File.open(@source, 'rb')
55
+ rewind
56
+ end
57
+
58
+ def read_from_file(bytes, output_buffer)
59
+ if bytes
60
+ data = @file.read([remaining_bytes, bytes].min)
61
+ data = nil if data == ''
62
+ else
63
+ data = @file.read(remaining_bytes)
64
+ end
65
+ @position += data ? data.bytesize : 0
66
+ output_buffer ? output_buffer.replace(data || '') : data
67
+ end
68
+
69
+ def remaining_bytes
70
+ @last_byte - @position
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,58 @@
1
+ require 'pathname'
2
+
3
+ module Aws
4
+ module S3
5
+ # @api private
6
+ class FileUploader
7
+
8
+ FIFTEEN_MEGABYTES = 15 * 1024 * 1024
9
+
10
+ # @option options [Client] :client
11
+ # @option options [Integer] :multipart_threshold Files greater than
12
+ # `:multipart_threshold` bytes are uploaded using S3 multipart APIs.
13
+ def initialize(options = {})
14
+ @options = options
15
+ @client = options[:client] || Client.new
16
+ @multipart_threshold = options[:multipart_threshold] || FIFTEEN_MEGABYTES
17
+ end
18
+
19
+ # @return [Client]
20
+ attr_reader :client
21
+
22
+ # @return [Integer] Files larger than this in bytes are uploaded
23
+ # using a {MultipartFileUploader}.
24
+ attr_reader :multipart_threshold
25
+
26
+ # @param [String,Pathname,File,Tempfile] source
27
+ # @option options [required,String] :bucket
28
+ # @option options [required,String] :key
29
+ # @return [void]
30
+ def upload(source, options = {})
31
+ if File.size(source) >= multipart_threshold
32
+ MultipartFileUploader.new(@options).upload(source, options)
33
+ else
34
+ put_object(source, options)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def put_object(source, options)
41
+ open_file(source) do |file|
42
+ @client.put_object(options.merge(body: file))
43
+ end
44
+ end
45
+
46
+ def open_file(source)
47
+ if String === source || Pathname === source
48
+ file = File.open(source, 'rb')
49
+ yield(file)
50
+ file.close
51
+ else
52
+ yield(source)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,186 @@
1
+ require 'set'
2
+ require 'time'
3
+ require 'openssl'
4
+ require 'webrick/httputils'
5
+ require 'aws-sdk-core/query'
6
+
7
+ module Aws
8
+ module S3
9
+ # @api private
10
+ class LegacySigner
11
+
12
+ SIGNED_QUERYSTRING_PARAMS = Set.new(%w(
13
+
14
+ acl delete cors lifecycle location logging notification partNumber
15
+ policy requestPayment restore tagging torrent uploadId uploads
16
+ versionId versioning versions website replication requestPayment
17
+ accelerate
18
+
19
+ response-content-type response-content-language
20
+ response-expires response-cache-control
21
+ response-content-disposition response-content-encoding
22
+
23
+ ))
24
+
25
+ def self.sign(context)
26
+ new(
27
+ context.config.credentials,
28
+ context.params,
29
+ context.config.force_path_style
30
+ ).sign(context.http_request)
31
+ end
32
+
33
+ # @param [CredentialProvider] credentials
34
+ def initialize(credentials, params, force_path_style)
35
+ @credentials = credentials.credentials
36
+ @params = Query::ParamList.new
37
+ params.each_pair do |param_name, param_value|
38
+ @params.set(param_name, param_value)
39
+ end
40
+ @force_path_style = force_path_style
41
+ end
42
+
43
+ attr_reader :credentials, :params
44
+
45
+ def sign(request)
46
+ if token = credentials.session_token
47
+ request.headers["X-Amz-Security-Token"] = token
48
+ end
49
+ request.headers['Authorization'] = authorization(request)
50
+ end
51
+
52
+ def authorization(request)
53
+ "AWS #{credentials.access_key_id}:#{signature(request)}"
54
+ end
55
+
56
+ def signature(request)
57
+ string_to_sign = string_to_sign(request)
58
+ signature = digest(credentials.secret_access_key, string_to_sign)
59
+ uri_escape(signature)
60
+ end
61
+
62
+ def digest(secret, string_to_sign)
63
+ Base64.encode64(hmac(secret, string_to_sign)).strip
64
+ end
65
+
66
+ def hmac(key, value)
67
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), key, value)
68
+ end
69
+
70
+ # From the S3 developer guide:
71
+ #
72
+ # StringToSign =
73
+ # HTTP-Verb ` "\n" `
74
+ # content-md5 ` "\n" `
75
+ # content-type ` "\n" `
76
+ # date ` "\n" `
77
+ # CanonicalizedAmzHeaders + CanonicalizedResource;
78
+ #
79
+ def string_to_sign(request)
80
+ [
81
+ request.http_method,
82
+ request.headers.values_at('Content-Md5', 'Content-Type').join("\n"),
83
+ signing_string_date(request),
84
+ canonicalized_headers(request),
85
+ canonicalized_resource(request.endpoint),
86
+ ].flatten.compact.join("\n")
87
+ end
88
+
89
+ def signing_string_date(request)
90
+ # if a date is provided via x-amz-date then we should omit the
91
+ # Date header from the signing string (should appear as a blank line)
92
+ if request.headers.detect{|k,v| k.to_s =~ /^x-amz-date$/i }
93
+ ''
94
+ else
95
+ request.headers['Date'] = Time.now.httpdate
96
+ end
97
+ end
98
+
99
+ # CanonicalizedAmzHeaders
100
+ #
101
+ # See the developer guide for more information on how this element
102
+ # is generated.
103
+ #
104
+ def canonicalized_headers(request)
105
+ x_amz = request.headers.select{|k, v| k =~ /^x-amz-/i }
106
+ x_amz = x_amz.collect{|k, v| [k.downcase, v] }
107
+ x_amz = x_amz.sort_by{|k, v| k }
108
+ x_amz = x_amz.collect{|k, v| "#{k}:#{v.to_s.strip}" }.join("\n")
109
+ x_amz == '' ? nil : x_amz
110
+ end
111
+
112
+ # From the S3 developer guide
113
+ #
114
+ # CanonicalizedResource =
115
+ # [ "/" ` Bucket ] `
116
+ # <HTTP-Request-URI, protocol name up to the querystring> +
117
+ # [ sub-resource, if present. e.g. "?acl", "?location",
118
+ # "?logging", or "?torrent"];
119
+ #
120
+ # @api private
121
+ def canonicalized_resource(endpoint)
122
+
123
+ parts = []
124
+
125
+ # virtual hosted-style requests require the hostname to appear
126
+ # in the canonicalized resource prefixed by a forward slash.
127
+ if bucket = params[:bucket]
128
+ bucket = bucket.value
129
+ ssl = endpoint.scheme == 'https'
130
+ if Plugins::BucketDns.dns_compatible?(bucket, ssl) && !@force_path_style
131
+ parts << "/#{bucket}"
132
+ end
133
+ end
134
+
135
+ # append the path name (no querystring)
136
+ parts << endpoint.path
137
+
138
+ # lastly any sub resource querystring params need to be appened
139
+ # in lexigraphical ordered joined by '&' and prefixed by '?'
140
+ params = signed_querystring_params(endpoint)
141
+
142
+ unless params.empty?
143
+ parts << '?'
144
+ parts << params.sort.collect{|p| p.to_s }.join('&')
145
+ end
146
+
147
+ parts.join
148
+ end
149
+
150
+ def signed_querystring_params(endpoint)
151
+ endpoint.query.to_s.split('&').select do |p|
152
+ SIGNED_QUERYSTRING_PARAMS.include?(p.split('=')[0])
153
+ end.map { |p| CGI.unescape(p) }
154
+ end
155
+
156
+ def uri_escape(s)
157
+
158
+ #URI.escape(s)
159
+
160
+ # URI.escape is deprecated, replacing it with escape from webrick
161
+ # to squelch the massive number of warnings generated from Ruby.
162
+ # The following script was used to determine the differences
163
+ # between the various escape methods available. The webrick
164
+ # escape only had two differences and it is available in the
165
+ # standard lib.
166
+ #
167
+ # (0..255).each {|c|
168
+ # s = [c].pack("C")
169
+ # e = [
170
+ # CGI.escape(s),
171
+ # ERB::Util.url_encode(s),
172
+ # URI.encode_www_form_component(s),
173
+ # WEBrick::HTTPUtils.escape_form(s),
174
+ # WEBrick::HTTPUtils.escape(s),
175
+ # URI.escape(s),
176
+ # ]
177
+ # next if e.uniq.length == 1
178
+ # puts("%5s %5s %5s %5s %5s %5s %5s" % ([s.inspect] + e))
179
+ # }
180
+ #
181
+ WEBrick::HTTPUtils.escape(s).gsub('%5B', '[').gsub('%5D', ']')
182
+ end
183
+
184
+ end
185
+ end
186
+ end