multipart-post 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7f48186ca2c1e47dbb18d8ba20ed99e1d2dbabac5735c47a5fdf3f3de2ff6c1
4
- data.tar.gz: 86bdb76a37261133d45eba29e94c6026118564fa0168120549d64a8cf89e19f7
3
+ metadata.gz: f1a26f1b06e253bdd1a96889881cd28090f1bb294754de989b5d01ee173f2251
4
+ data.tar.gz: f0df38197a55f9470d21d0284fb12c16deabe86e1ab71e39f409bcbabfb9cf86
5
5
  SHA512:
6
- metadata.gz: 47022f82aba55743549c58e82cda833a1f769927ca7c348b35e8dbcfeec851ca5570ef4b43755d33c4d5957a97b915412e86d9b333769f5cd11d0b9cdeede744
7
- data.tar.gz: c9c767b946971349722a5798b0594f9da22095262f0f322391656dc79e73a677a48b0fdc53942f5bfbfe58d258c7e6df8bd3fb02821b49a64f794c6f898012d5
6
+ metadata.gz: 8048da79e851331b26fae693ce8e5329481c729934f3653fce804332df8b3f8edcdc566dc543a14e461ec674bb0700e87b92d26b5fe24dc4fcb13312574faf04
7
+ data.tar.gz: 1323dbcffd530f36bbb14e7ca5f90033ce3bf558ef669387df855638da7a6a619b4fe355ab5b2470428edfd2bb5c1fdbd267b848a940d84a0bbcb451c341c743
checksums.yaml.gz.sig ADDED
Binary file
data/changelog.md ADDED
@@ -0,0 +1,14 @@
1
+ # CHANGELOG
2
+
3
+ ## Unreleased
4
+
5
+ ### Added
6
+
7
+ - Add the ability to set Content-ID header for ParamPart [#62](https://github.com/socketry/multipart-post/pull/62)
8
+ - Allow mixed key types for parts headers [#79](https://github.com/socketry/multipart-post/pull/79)
9
+
10
+ ### Changed
11
+
12
+ - Refactor `Parts` into a `Multipart::Post` namespace [#65](https://github.com/socketry/multipart-post/pull/65)
13
+ - Use mutable strings where needed [#70](https://github.com/socketry/multipart-post/pull/70)
14
+ - Use `frozen_string_literal` everywhere [#78](https://github.com/socketry/multipart-post/pull/78)
data/lib/composite_io.rb CHANGED
@@ -1,108 +1,17 @@
1
- #--
2
- # Copyright (c) 2007-2012 Nick Sieger.
3
- # See the file README.txt included with the distribution for
4
- # software license details.
5
- #++
6
-
7
- # Concatenate together multiple IO objects into a single, composite IO object
8
- # for purposes of reading as a single stream.
9
- #
10
- # @example
11
- # crio = CompositeReadIO.new(StringIO.new('one'),
12
- # StringIO.new('two'),
13
- # StringIO.new('three'))
14
- # puts crio.read # => "onetwothree"
15
- class CompositeReadIO
16
- # Create a new composite-read IO from the arguments, all of which should
17
- # respond to #read in a manner consistent with IO.
18
- def initialize(*ios)
19
- @ios = ios.flatten
20
- @index = 0
21
- end
22
-
23
- # Read from IOs in order until `length` bytes have been received.
24
- def read(length = nil, outbuf = nil)
25
- got_result = false
26
- outbuf = outbuf ? outbuf.replace("") : ""
27
-
28
- while io = current_io
29
- if result = io.read(length)
30
- got_result ||= !result.nil?
31
- result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
32
- outbuf << result
33
- length -= result.length if length
34
- break if length == 0
35
- end
36
- advance_io
37
- end
38
- (!got_result && length) ? nil : outbuf
39
- end
40
-
41
- def rewind
42
- @ios.each { |io| io.rewind }
43
- @index = 0
44
- end
45
-
46
- private
47
-
48
- def current_io
49
- @ios[@index]
50
- end
51
-
52
- def advance_io
53
- @index += 1
54
- end
55
- end
56
-
57
- # Convenience methods for dealing with files and IO that are to be uploaded.
58
- class UploadIO
59
- attr_reader :content_type, :original_filename, :local_path, :io, :opts
60
-
61
- # Create an upload IO suitable for including in the params hash of a
62
- # Net::HTTP::Post::Multipart.
63
- #
64
- # Can take two forms. The first accepts a filename and content type, and
65
- # opens the file for reading (to be closed by finalizer).
66
- #
67
- # The second accepts an already-open IO, but also requires a third argument,
68
- # the filename from which it was opened (particularly useful/recommended if
69
- # uploading directly from a form in a framework, which often save the file to
70
- # an arbitrarily named RackMultipart file in /tmp).
71
- #
72
- # @example
73
- # UploadIO.new("file.txt", "text/plain")
74
- # UploadIO.new(file_io, "text/plain", "file.txt")
75
- def initialize(filename_or_io, content_type, filename = nil, opts = {})
76
- io = filename_or_io
77
- local_path = ""
78
- if io.respond_to? :read
79
- # in Ruby 1.9.2, StringIOs no longer respond to path
80
- # (since they respond to :length, so we don't need their local path, see parts.rb:41)
81
- local_path = filename_or_io.respond_to?(:path) ? filename_or_io.path : "local.path"
82
- else
83
- io = File.open(filename_or_io)
84
- local_path = filename_or_io
85
- end
86
- filename ||= local_path
87
-
88
- @content_type = content_type
89
- @original_filename = File.basename(filename)
90
- @local_path = local_path
91
- @io = io
92
- @opts = opts
93
- end
94
-
95
- def self.convert!(io, content_type, original_filename, local_path)
96
- raise ArgumentError, "convert! has been removed. You must now wrap IOs " \
97
- "using:\nUploadIO.new(filename_or_io, content_type, " \
98
- "filename=nil)\nPlease update your code."
99
- end
100
-
101
- def method_missing(*args)
102
- @io.send(*args)
103
- end
104
-
105
- def respond_to?(meth, include_all = false)
106
- @io.respond_to?(meth, include_all) || super(meth, include_all)
107
- end
108
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2006-2013, by Nick Sieger.
5
+ # Copyright, 2010, by Tohru Hashimoto.
6
+ # Copyright, 2011, by Jeff Hodges.
7
+ # Copyright, 2011, by Alex Koppel.
8
+ # Copyright, 2011, by Christine Yen.
9
+ # Copyright, 2011, by Gerrit Riessen.
10
+ # Copyright, 2011, by Luke Redpath.
11
+ # Copyright, 2013, by Mislav Marohnić.
12
+ # Copyright, 2013, by Leo Cassarani.
13
+ # Copyright, 2019, by Olle Jonsson.
14
+ # Copyright, 2022, by Samuel Williams.
15
+
16
+ warn "Top level ::CompositeIO is deprecated, require 'multipart/post' and use `Multipart::Post::CompositeReadIO` instead!"
17
+ require_relative 'multipart/post'
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2006-2013, by Nick Sieger.
5
+ # Copyright, 2010, by Tohru Hashimoto.
6
+ # Copyright, 2011, by Jeff Hodges.
7
+ # Copyright, 2011, by Alex Koppel.
8
+ # Copyright, 2011, by Christine Yen.
9
+ # Copyright, 2011, by Gerrit Riessen.
10
+ # Copyright, 2011, by Luke Redpath.
11
+ # Copyright, 2013, by Mislav Marohnić.
12
+ # Copyright, 2013, by Leo Cassarani.
13
+ # Copyright, 2019, by Olle Jonsson.
14
+ # Copyright, 2019, by Patrick Davey.
15
+ # Copyright, 2021, by Lewis Cowles.
16
+ # Copyright, 2021-2022, by Samuel Williams.
17
+
18
+ module Multipart
19
+ module Post
20
+ # Concatenate together multiple IO objects into a single, composite IO object
21
+ # for purposes of reading as a single stream.
22
+ #
23
+ # @example
24
+ # crio = CompositeReadIO.new(StringIO.new('one'),
25
+ # StringIO.new('two'),
26
+ # StringIO.new('three'))
27
+ # puts crio.read # => "onetwothree"
28
+ class CompositeReadIO
29
+ # Create a new composite-read IO from the arguments, all of which should
30
+ # respond to #read in a manner consistent with IO.
31
+ def initialize(*ios)
32
+ @ios = ios.flatten
33
+ @index = 0
34
+ end
35
+
36
+ # Read from IOs in order until `length` bytes have been received.
37
+ def read(length = nil, outbuf = nil)
38
+ got_result = false
39
+ outbuf = outbuf ? outbuf.replace("") : String.new
40
+
41
+ while io = current_io
42
+ if result = io.read(length)
43
+ got_result ||= !result.nil?
44
+ result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
45
+ outbuf << result
46
+ length -= result.length if length
47
+ break if length == 0
48
+ end
49
+ advance_io
50
+ end
51
+ (!got_result && length) ? nil : outbuf
52
+ end
53
+
54
+ def rewind
55
+ @ios.each { |io| io.rewind }
56
+ @index = 0
57
+ end
58
+
59
+ private
60
+
61
+ def current_io
62
+ @ios[@index]
63
+ end
64
+
65
+ def advance_io
66
+ @index += 1
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ CompositeIO = Multipart::Post::CompositeReadIO
73
+ Object.deprecate_constant :CompositeIO
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2008, by McClain Looney.
5
+ # Copyright, 2008-2013, by Nick Sieger.
6
+ # Copyright, 2011, by Gerrit Riessen.
7
+ # Copyright, 2013, by Vincent Pellé.
8
+ # Copyright, 2013, by Gustav Ernberg.
9
+ # Copyright, 2013, by Socrates Vicente.
10
+ # Copyright, 2013, by Steffen Grunwald.
11
+ # Copyright, 2019, by Olle Jonsson.
12
+ # Copyright, 2019-2022, by Samuel Williams.
13
+ # Copyright, 2019, by Patrick Davey.
14
+ # Copyright, 2022, by Jason York.
15
+
16
+ require_relative 'parts'
17
+ require_relative 'composite_read_io'
18
+
19
+ require 'securerandom'
20
+
21
+ module Multipart
22
+ module Post
23
+ module Multipartable
24
+ def self.secure_boundary
25
+ # https://tools.ietf.org/html/rfc7230
26
+ # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
27
+ # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
28
+ # / DIGIT / ALPHA
29
+
30
+ # https://tools.ietf.org/html/rfc2046
31
+ # bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
32
+ # "+" / "_" / "," / "-" / "." /
33
+ # "/" / ":" / "=" / "?"
34
+
35
+ "--#{SecureRandom.uuid}"
36
+ end
37
+
38
+ def initialize(path, params, headers={}, boundary = Multipartable.secure_boundary)
39
+ headers = headers.clone # don't want to modify the original variable
40
+ parts_headers = symbolize_keys(headers.delete(:parts) || {})
41
+
42
+ super(path, headers)
43
+ parts = symbolize_keys(params).map do |k,v|
44
+ case v
45
+ when Array
46
+ v.map {|item| Parts::Part.new(boundary, k, item, parts_headers[k]) }
47
+ else
48
+ Parts::Part.new(boundary, k, v, parts_headers[k])
49
+ end
50
+ end.flatten
51
+ parts << Parts::EpiloguePart.new(boundary)
52
+ ios = parts.map {|p| p.to_io }
53
+ self.set_content_type(headers["Content-Type"] || "multipart/form-data",
54
+ { "boundary" => boundary })
55
+ self.content_length = parts.inject(0) {|sum,i| sum + i.length }
56
+ self.body_stream = CompositeReadIO.new(*ios)
57
+
58
+ @boundary = boundary
59
+ end
60
+
61
+ attr :boundary
62
+
63
+ private
64
+
65
+ if RUBY_VERSION >= "2.5.0"
66
+ def symbolize_keys(hash)
67
+ hash.transform_keys(&:to_sym)
68
+ end
69
+ else
70
+ def symbolize_keys(hash)
71
+ hash.map{|key,value| [key.to_sym, value]}.to_h
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2008-2009, by McClain Looney.
5
+ # Copyright, 2009-2013, by Nick Sieger.
6
+ # Copyright, 2011, by Johannes Wagener.
7
+ # Copyright, 2011, by Gerrit Riessen.
8
+ # Copyright, 2011, by Jason Moore.
9
+ # Copyright, 2012, by Steven Davidovitz.
10
+ # Copyright, 2012, by hexfet.
11
+ # Copyright, 2013, by Vincent Pellé.
12
+ # Copyright, 2013, by Gustav Ernberg.
13
+ # Copyright, 2013, by Socrates Vicente.
14
+ # Copyright, 2017, by David Moles.
15
+ # Copyright, 2017, by Matt Colyer.
16
+ # Copyright, 2017, by Eric Hutzelman.
17
+ # Copyright, 2019-2021, by Olle Jonsson.
18
+ # Copyright, 2019, by Ethan Turkeltaub.
19
+ # Copyright, 2019, by Patrick Davey.
20
+ # Copyright, 2021-2022, by Samuel Williams.
21
+
22
+ require 'stringio'
23
+
24
+ module Multipart
25
+ module Post
26
+ module Parts
27
+ module Part
28
+ def self.new(boundary, name, value, headers = {})
29
+ headers ||= {} # avoid nil values
30
+ if file?(value)
31
+ FilePart.new(boundary, name, value, headers)
32
+ else
33
+ ParamPart.new(boundary, name, value, headers)
34
+ end
35
+ end
36
+
37
+ def self.file?(value)
38
+ value.respond_to?(:content_type) && value.respond_to?(:original_filename)
39
+ end
40
+
41
+ def length
42
+ @part.length
43
+ end
44
+
45
+ def to_io
46
+ @io
47
+ end
48
+ end
49
+
50
+ # Represents a parametric part to be filled with given value.
51
+ class ParamPart
52
+ include Part
53
+
54
+ # @param boundary [String]
55
+ # @param name [#to_s]
56
+ # @param value [String]
57
+ # @param headers [Hash] Content-Type and Content-ID are used, if present.
58
+ def initialize(boundary, name, value, headers = {})
59
+ @part = build_part(boundary, name, value, headers)
60
+ @io = StringIO.new(@part)
61
+ end
62
+
63
+ def length
64
+ @part.bytesize
65
+ end
66
+
67
+ # @param boundary [String]
68
+ # @param name [#to_s]
69
+ # @param value [String]
70
+ # @param headers [Hash] Content-Type is used, if present.
71
+ def build_part(boundary, name, value, headers = {})
72
+ part = String.new
73
+ part << "--#{boundary}\r\n"
74
+ part << "Content-ID: #{headers["Content-ID"]}\r\n" if headers["Content-ID"]
75
+ part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n"
76
+ part << "Content-Type: #{headers["Content-Type"]}\r\n" if headers["Content-Type"]
77
+ part << "\r\n"
78
+ part << "#{value}\r\n"
79
+ end
80
+ end
81
+
82
+ # Represents a part to be filled from file IO.
83
+ class FilePart
84
+ include Part
85
+
86
+ attr_reader :length
87
+
88
+ # @param boundary [String]
89
+ # @param name [#to_s]
90
+ # @param io [IO]
91
+ # @param headers [Hash]
92
+ def initialize(boundary, name, io, headers = {})
93
+ file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path)
94
+ @head = build_head(boundary, name, io.original_filename, io.content_type, file_length,
95
+ io.respond_to?(:opts) ? io.opts.merge(headers) : headers)
96
+ @foot = "\r\n"
97
+ @length = @head.bytesize + file_length + @foot.length
98
+ @io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new(@foot))
99
+ end
100
+
101
+ # @param boundary [String]
102
+ # @param name [#to_s]
103
+ # @param filename [String]
104
+ # @param type [String]
105
+ # @param content_len [Integer]
106
+ # @param opts [Hash]
107
+ def build_head(boundary, name, filename, type, content_len, opts = {})
108
+ opts = opts.clone
109
+
110
+ trans_encoding = opts.delete("Content-Transfer-Encoding") || "binary"
111
+ content_disposition = opts.delete("Content-Disposition") || "form-data"
112
+
113
+ part = String.new
114
+ part << "--#{boundary}\r\n"
115
+ part << "Content-Disposition: #{content_disposition}; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n"
116
+ part << "Content-Length: #{content_len}\r\n"
117
+ if content_id = opts.delete("Content-ID")
118
+ part << "Content-ID: #{content_id}\r\n"
119
+ end
120
+
121
+ if opts["Content-Type"] != nil
122
+ part << "Content-Type: " + opts["Content-Type"] + "\r\n"
123
+ else
124
+ part << "Content-Type: #{type}\r\n"
125
+ end
126
+
127
+ part << "Content-Transfer-Encoding: #{trans_encoding}\r\n"
128
+
129
+ opts.each do |k, v|
130
+ part << "#{k}: #{v}\r\n"
131
+ end
132
+
133
+ part << "\r\n"
134
+ end
135
+ end
136
+
137
+ # Represents the epilogue or closing boundary.
138
+ class EpiloguePart
139
+ include Part
140
+
141
+ def initialize(boundary)
142
+ @part = String.new("--#{boundary}--\r\n")
143
+ @io = StringIO.new(@part)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022, by Samuel Williams.
5
+
6
+ module Multipart
7
+ module Post
8
+ # Convenience methods for dealing with files and IO that are to be uploaded.
9
+ class UploadIO
10
+ attr_reader :content_type, :original_filename, :local_path, :io, :opts
11
+
12
+ # Create an upload IO suitable for including in the params hash of a
13
+ # Net::HTTP::Post::Multipart.
14
+ #
15
+ # Can take two forms. The first accepts a filename and content type, and
16
+ # opens the file for reading (to be closed by finalizer).
17
+ #
18
+ # The second accepts an already-open IO, but also requires a third argument,
19
+ # the filename from which it was opened (particularly useful/recommended if
20
+ # uploading directly from a form in a framework, which often save the file to
21
+ # an arbitrarily named RackMultipart file in /tmp).
22
+ #
23
+ # @example
24
+ # UploadIO.new("file.txt", "text/plain")
25
+ # UploadIO.new(file_io, "text/plain", "file.txt")
26
+ def initialize(filename_or_io, content_type, filename = nil, opts = {})
27
+ io = filename_or_io
28
+ local_path = ""
29
+ if io.respond_to? :read
30
+ # in Ruby 1.9.2, StringIOs no longer respond to path
31
+ # (since they respond to :length, so we don't need their local path, see parts.rb:41)
32
+ local_path = filename_or_io.respond_to?(:path) ? filename_or_io.path : "local.path"
33
+ else
34
+ io = File.open(filename_or_io)
35
+ local_path = filename_or_io
36
+ end
37
+ filename ||= local_path
38
+
39
+ @content_type = content_type
40
+ @original_filename = File.basename(filename)
41
+ @local_path = local_path
42
+ @io = io
43
+ @opts = opts
44
+ end
45
+
46
+ def self.convert!(io, content_type, original_filename, local_path)
47
+ raise ArgumentError, "convert! has been removed. You must now wrap IOs " \
48
+ "using:\nUploadIO.new(filename_or_io, content_type, " \
49
+ "filename=nil)\nPlease update your code."
50
+ end
51
+
52
+ def method_missing(*args)
53
+ @io.send(*args)
54
+ end
55
+
56
+ def respond_to?(meth, include_all = false)
57
+ @io.respond_to?(meth, include_all) || super(meth, include_all)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ UploadIO = Multipart::Post::UploadIO
64
+ Object.deprecate_constant :UploadIO
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2019, by Patrick Davey.
5
+ # Copyright, 2021-2023, by Samuel Williams.
6
+
7
+ module Multipart
8
+ module Post
9
+ VERSION = "2.3.0"
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2019, by Patrick Davey.
5
+ # Copyright, 2021-2022, by Samuel Williams.
6
+
7
+ require_relative 'post/multipartable'
8
+ require_relative 'post/upload_io'
@@ -1,9 +1,11 @@
1
- #--
2
- # Copyright (c) 2007-2013 Nick Sieger.
3
- # See the file README.txt included with the distribution for
4
- # software license details.
5
- #++
1
+ # frozen_string_literal: true
6
2
 
7
- module MultipartPost
8
- VERSION = "2.1.0"
9
- end
3
+ # Released under the MIT License.
4
+ # Copyright, 2009-2013, by Nick Sieger.
5
+ # Copyright, 2019-2022, by Samuel Williams.
6
+
7
+ warn "Top level ::MultipartPost is deprecated, require 'multipart/post' and use `Multipart::Post` instead!"
8
+ require_relative 'multipart/post'
9
+
10
+ MultipartPost = Multipart::Post
11
+ Object.deprecate_constant :MultipartPost
data/lib/multipartable.rb CHANGED
@@ -1,48 +1,19 @@
1
- #--
2
- # Copyright (c) 2007-2013 Nick Sieger.
3
- # See the file README.txt included with the distribution for
4
- # software license details.
5
- #++
1
+ # frozen_string_literal: true
6
2
 
7
- require 'parts'
8
- require 'securerandom'
3
+ # Released under the MIT License.
4
+ # Copyright, 2008, by McClain Looney.
5
+ # Copyright, 2008-2013, by Nick Sieger.
6
+ # Copyright, 2011, by Gerrit Riessen.
7
+ # Copyright, 2013, by Vincent Pellé.
8
+ # Copyright, 2013, by Gustav Ernberg.
9
+ # Copyright, 2013, by Socrates Vicente.
10
+ # Copyright, 2013, by Steffen Grunwald.
11
+ # Copyright, 2019, by Olle Jonsson.
12
+ # Copyright, 2019-2022, by Samuel Williams.
13
+ # Copyright, 2019, by Patrick Davey.
9
14
 
10
- module Multipartable
11
- def self.secure_boundary
12
- # https://tools.ietf.org/html/rfc7230
13
- # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
14
- # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
15
- # / DIGIT / ALPHA
16
-
17
- # https://tools.ietf.org/html/rfc2046
18
- # bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
19
- # "+" / "_" / "," / "-" / "." /
20
- # "/" / ":" / "=" / "?"
21
-
22
- "--#{SecureRandom.alphanumeric(60)}"
23
- end
24
-
25
- def initialize(path, params, headers={}, boundary = Multipartable.secure_boundary)
26
- headers = headers.clone # don't want to modify the original variable
27
- parts_headers = headers.delete(:parts) || {}
28
- super(path, headers)
29
- parts = params.map do |k,v|
30
- case v
31
- when Array
32
- v.map {|item| Parts::Part.new(boundary, k, item, parts_headers[k]) }
33
- else
34
- Parts::Part.new(boundary, k, v, parts_headers[k])
35
- end
36
- end.flatten
37
- parts << Parts::EpiloguePart.new(boundary)
38
- ios = parts.map {|p| p.to_io }
39
- self.set_content_type(headers["Content-Type"] || "multipart/form-data",
40
- { "boundary" => boundary })
41
- self.content_length = parts.inject(0) {|sum,i| sum + i.length }
42
- self.body_stream = CompositeReadIO.new(*ios)
43
-
44
- @boundary = boundary
45
- end
46
-
47
- attr :boundary
48
- end
15
+ warn "Top level ::Multipartable is deprecated, require 'multipart/post' and use `Multipart::Post::Multipartable` instead!"
16
+ require_relative 'multipart/post'
17
+
18
+ Multipartable = Multipart::Post::Multipartable
19
+ Object.deprecate_constant :Multipartable
@@ -1,27 +1,27 @@
1
- #--
2
- # Copyright (c) 2007-2012 Nick Sieger.
3
- # See the file README.txt included with the distribution for
4
- # software license details.
5
- #++
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2006-2012, by Nick Sieger.
5
+ # Copyright, 2008, by McClain Looney.
6
+ # Copyright, 2019, by Olle Jonsson.
7
+ # Copyright, 2019, by Patrick Davey.
8
+ # Copyright, 2021-2022, by Samuel Williams.
6
9
 
7
10
  require 'net/http'
8
- require 'stringio'
9
- require 'cgi'
10
- require 'composite_io'
11
- require 'multipartable'
12
- require 'parts'
11
+
12
+ require_relative '../../../multipart/post'
13
13
 
14
14
  module Net
15
15
  class HTTP
16
16
  class Put
17
17
  class Multipart < Put
18
- include Multipartable
18
+ include ::Multipart::Post::Multipartable
19
19
  end
20
20
  end
21
21
 
22
22
  class Post
23
23
  class Multipart < Post
24
- include Multipartable
24
+ include ::Multipart::Post::Multipartable
25
25
  end
26
26
  end
27
27
  end