aws-sdk-s3 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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