multipart-post 1.1.5 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e64b8bb7510028e6d5c5ca9626ff5448a12ee4e393938a69edd9eddfedf04e70
4
+ data.tar.gz: dddcaa65fa823b59d0d3a025c8ad1e88eae0e2fe976469c9b69931a0bc66af6a
5
+ SHA512:
6
+ metadata.gz: a046600778502bf34933ca1f4b2abd7ce0c7e8b4911ab9abc2bc74316cd42ce2f9167c6a40254ffb290ede586de81500a03500c09bfcb15ed96c0495bed19964
7
+ data.tar.gz: 422aa7086f923b29e545cba81cd1caca270ed4e86f0d29a50a8c61bc111ad30d546b353ef3db5eb52f63f697d03a5671e759f3119c8c200807184c9cfe3c1f58
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg
3
3
  *~
4
4
  *.swo
5
5
  *.swp
6
+ /Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --require spec_helper
5
+ --warnings
@@ -1,7 +1,20 @@
1
+ language: ruby
2
+ cache: bundler
3
+
1
4
  rvm:
2
- - 1.8.7
3
- - 1.9.2
4
- - jruby
5
- branches:
6
- only:
7
- - master
5
+ - 2.0
6
+ - 2.1
7
+ - 2.2
8
+ - 2.3
9
+ - 2.4
10
+ - 2.5
11
+ - 2.6
12
+ - ruby-head
13
+ - jruby-head
14
+ - truffleruby
15
+
16
+ matrix:
17
+ allow_failures:
18
+ - rvm: ruby-head
19
+ - rvm: jruby-head
20
+ - rvm: truffleruby
@@ -0,0 +1,6 @@
1
+ --no-private
2
+ lib/**/*.rb
3
+ -
4
+ History.txt
5
+ LICENSE
6
+ README.md
data/Gemfile CHANGED
@@ -1,14 +1,6 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
- platforms :mri_19 do
5
- gem 'ruby-debug19'
6
- end
7
-
8
- platforms :mri_18 do
9
- gem 'ruby-debug'
10
- end
11
-
12
4
  group :development, :test do
13
5
  gem 'rake'
14
6
  end
@@ -1,3 +1,25 @@
1
+ <!--
2
+ # @markup rdoc
3
+ # @title CHANGELOG
4
+ -->
5
+
6
+ === 2.0.0 / 2013-12-21
7
+
8
+ - Drop Ruby 1.8 compatibility
9
+ - GH #21: Fix FilePart length calculation for Ruby 1.9 when filename contains
10
+ multibyte characters (hexfet)
11
+ - GH #20: Ensure upload responds to both #content_type and #original_filename
12
+ (Steven Davidovitz)
13
+ - GH #31: Support setting headers on any part of the request (Socrates Vicente)
14
+ - GH #30: Support array values for params (Gustav Ernberg)
15
+ - GH #32: Fix respond_to? signature (Leo Cassarani)
16
+ - GH #33: Update README to markdown (Jagtesh Chadha)
17
+ - GH #35: Improved handling of array-type parameters (Steffen Grunwald)
18
+
19
+ === 1.2.0 / 2013-02-25
20
+
21
+ - #25: Ruby 2 compatibility (thanks mislav)
22
+
1
23
  === 1.1.5 / 2012-02-12
2
24
 
3
25
  - Fix length/bytesize of parts in 1.9 (#7, #14) (Jason Moore)
@@ -40,4 +62,3 @@
40
62
  * 1 major enhancement
41
63
 
42
64
  * Birthday!
43
-
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2007-2013 Nick Sieger nick@nicksieger.com
2
+ Copyright, 2017, by Samuel G. D. Williams.
3
+
4
+ MIT license.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ this software and associated documentation files (the "Software"), to deal in
8
+ the Software without restriction, including without limitation the rights to
9
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ the Software, and to permit persons to whom the Software is furnished to do so,
11
+ subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,127 @@
1
+ # Multipart::Post
2
+
3
+ Adds a streamy multipart form post capability to `Net::HTTP`. Also supports other
4
+ methods besides `POST`.
5
+
6
+ [![Build Status](https://secure.travis-ci.org/socketry/multipart-post.svg)](http://travis-ci.org/socketry/multipart-post)
7
+
8
+ ## Features/Problems
9
+
10
+ * Appears to actually work. A good feature to have.
11
+ * Encapsulates posting of file/binary parts and name/value parameter parts, similar to
12
+ most browsers' file upload forms.
13
+ * Provides an `UploadIO` helper class to prepare IO objects for inclusion in the params
14
+ hash of the multipart post object.
15
+
16
+ ## Installation
17
+
18
+ gem install multipart-post
19
+
20
+ or in your Gemfile
21
+
22
+ gem 'multipart-post'
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ require 'net/http/post/multipart'
28
+
29
+ url = URI.parse('http://www.example.com/upload')
30
+ File.open("./image.jpg") do |jpg|
31
+ req = Net::HTTP::Post::Multipart.new url.path,
32
+ "file" => UploadIO.new(jpg, "image/jpeg", "image.jpg")
33
+ res = Net::HTTP.start(url.host, url.port) do |http|
34
+ http.request(req)
35
+ end
36
+ end
37
+ ```
38
+
39
+ To post multiple files or attachments, simply include multiple parameters with
40
+ `UploadIO` values:
41
+
42
+ ```ruby
43
+ require 'net/http/post/multipart'
44
+
45
+ url = URI.parse('http://www.example.com/upload')
46
+ req = Net::HTTP::Post::Multipart.new url.path,
47
+ "file1" => UploadIO.new(File.new("./image.jpg"), "image/jpeg", "image.jpg"),
48
+ "file2" => UploadIO.new(File.new("./image2.jpg"), "image/jpeg", "image2.jpg")
49
+ res = Net::HTTP.start(url.host, url.port) do |http|
50
+ http.request(req)
51
+ end
52
+ ```
53
+
54
+ To post files with other normal, non-file params such as input values, you need to pass hashes to the `Multipart.new` method.
55
+
56
+ In Rails 4 for example:
57
+
58
+ ```ruby
59
+ def model_params
60
+ require_params = params.require(:model).permit(:param_one, :param_two, :param_three, :avatar)
61
+ require_params[:avatar] = model_params[:avatar].present? ? UploadIO.new(model_params[:avatar].tempfile, model_params[:avatar].content_type, model_params[:avatar].original_filename) : nil
62
+ require_params
63
+ end
64
+
65
+ require 'net/http/post/multipart'
66
+
67
+ url = URI.parse('http://www.example.com/upload')
68
+ Net::HTTP.start(url.host, url.port) do |http|
69
+ req = Net::HTTP::Post::Multipart.new(url, model_params)
70
+ key = "authorization_key"
71
+ req.add_field("Authorization", key) #add to Headers
72
+ http.use_ssl = (url.scheme == "https")
73
+ http.request(req)
74
+ end
75
+ ```
76
+
77
+ Or in plain ruby:
78
+
79
+ ```ruby
80
+ def params(file)
81
+ params = { "description" => "A nice picture!" }
82
+ params[:datei] = UploadIO.new(file, "image/jpeg", "image.jpg")
83
+ params
84
+ end
85
+
86
+ url = URI.parse('http://www.example.com/upload')
87
+ File.open("./image.jpg") do |file|
88
+ req = Net::HTTP::Post::Multipart.new(url.path, params(file))
89
+ res = Net::HTTP.start(url.host, url.port) do |http|
90
+ return http.request(req).body
91
+ end
92
+ end
93
+ ```
94
+
95
+ ### Debugging
96
+
97
+ You can debug requests and responses (e.g. status codes) for all requests by adding the following code:
98
+
99
+ ```ruby
100
+ http = Net::HTTP.new(uri.host, uri.port)
101
+ http.set_debug_output($stdout)
102
+ ```
103
+
104
+ ## License
105
+
106
+ Released under the MIT license.
107
+
108
+ Copyright (c) 2007-2013 Nick Sieger <nick@nicksieger.com>
109
+ Copyright, 2017, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
110
+
111
+ Permission is hereby granted, free of charge, to any person obtaining a copy
112
+ of this software and associated documentation files (the "Software"), to deal
113
+ in the Software without restriction, including without limitation the rights
114
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
115
+ copies of the Software, and to permit persons to whom the Software is
116
+ furnished to do so, subject to the following conditions:
117
+
118
+ The above copyright notice and this permission notice shall be included in
119
+ all copies or substantial portions of the Software.
120
+
121
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
122
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
123
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
124
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
125
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
126
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
127
+ THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,9 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
2
3
 
3
- task :default => :test
4
+ RSpec::Core::RakeTask.new(:test)
4
5
 
5
- require 'rake/testtask'
6
- Rake::TestTask.new do |t|
7
- t.libs << "test"
8
- t.test_files = FileList['test/**/test*.rb']
9
- end
6
+ task :default => :test
@@ -7,54 +7,57 @@
7
7
  # Concatenate together multiple IO objects into a single, composite IO object
8
8
  # for purposes of reading as a single stream.
9
9
  #
10
- # Usage:
11
- #
12
- # crio = CompositeReadIO.new(StringIO.new('one'), StringIO.new('two'), StringIO.new('three'))
10
+ # @example
11
+ # crio = CompositeReadIO.new(StringIO.new('one'),
12
+ # StringIO.new('two'),
13
+ # StringIO.new('three'))
13
14
  # puts crio.read # => "onetwothree"
14
- #
15
15
  class CompositeReadIO
16
16
  # Create a new composite-read IO from the arguments, all of which should
17
17
  # respond to #read in a manner consistent with IO.
18
18
  def initialize(*ios)
19
19
  @ios = ios.flatten
20
+ @index = 0
20
21
  end
21
22
 
22
- # Read from the IO object, overlapping across underlying streams as necessary.
23
- def read(amount = nil, buf = nil)
24
- buffer = buf || ''
25
- done = if amount; nil; else ''; end
26
- partial_amount = amount
27
- parts = @ios.dup
28
-
29
- loop do
30
- result = done
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("") : ""
31
27
 
32
- while !parts.empty? && (result = parts.first.read(partial_amount)) == done
33
- parts.shift
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
34
35
  end
35
-
36
- result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
37
- buffer << result if result
38
- partial_amount -= result.length if partial_amount && result != done
39
-
40
- break if partial_amount && partial_amount <= 0
41
- break if result == done
42
- end
43
-
44
- if buffer.length > 0
45
- buffer
46
- else
47
- done
36
+ advance_io
48
37
  end
38
+ (!got_result && length) ? nil : outbuf
49
39
  end
50
-
40
+
51
41
  def rewind
52
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
53
54
  end
54
55
  end
55
56
 
56
57
  # Convenience methods for dealing with files and IO that are to be uploaded.
57
58
  class UploadIO
59
+ attr_reader :content_type, :original_filename, :local_path, :io, :opts
60
+
58
61
  # Create an upload IO suitable for including in the params hash of a
59
62
  # Net::HTTP::Post::Multipart.
60
63
  #
@@ -66,13 +69,9 @@ class UploadIO
66
69
  # uploading directly from a form in a framework, which often save the file to
67
70
  # an arbitrarily named RackMultipart file in /tmp).
68
71
  #
69
- # Usage:
70
- #
72
+ # @example
71
73
  # UploadIO.new("file.txt", "text/plain")
72
74
  # UploadIO.new(file_io, "text/plain", "file.txt")
73
- #
74
- attr_reader :content_type, :original_filename, :local_path, :io, :opts
75
-
76
75
  def initialize(filename_or_io, content_type, filename = nil, opts = {})
77
76
  io = filename_or_io
78
77
  local_path = ""
@@ -94,14 +93,16 @@ class UploadIO
94
93
  end
95
94
 
96
95
  def self.convert!(io, content_type, original_filename, local_path)
97
- raise ArgumentError, "convert! has been removed. You must now wrap IOs using:\nUploadIO.new(filename_or_io, content_type, filename=nil)\nPlease update your code."
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."
98
99
  end
99
100
 
100
101
  def method_missing(*args)
101
102
  @io.send(*args)
102
103
  end
103
104
 
104
- def respond_to?(meth)
105
- @io.respond_to?(meth) || super(meth)
105
+ def respond_to?(meth, include_all = false)
106
+ @io.respond_to?(meth, include_all) || super(meth, include_all)
106
107
  end
107
108
  end
@@ -1,9 +1,9 @@
1
1
  #--
2
- # Copyright (c) 2007-2012 Nick Sieger.
2
+ # Copyright (c) 2007-2013 Nick Sieger.
3
3
  # See the file README.txt included with the distribution for
4
4
  # software license details.
5
5
  #++
6
6
 
7
7
  module MultipartPost
8
- VERSION = "1.1.5"
8
+ VERSION = "2.1.1"
9
9
  end
@@ -1,20 +1,48 @@
1
1
  #--
2
- # Copyright (c) 2007-2012 Nick Sieger.
2
+ # Copyright (c) 2007-2013 Nick Sieger.
3
3
  # See the file README.txt included with the distribution for
4
4
  # software license details.
5
5
  #++
6
6
 
7
7
  require 'parts'
8
- module Multipartable
9
- DEFAULT_BOUNDARY = "-----------RubyMultipartPost"
10
- def initialize(path, params, headers={}, boundary = DEFAULT_BOUNDARY)
11
- super(path, headers)
12
- parts = params.map {|k,v| Parts::Part.new(boundary, k, v)}
13
- parts << Parts::EpiloguePart.new(boundary)
14
- ios = parts.map{|p| p.to_io }
15
- self.set_content_type(headers["Content-Type"] || "multipart/form-data",
16
- { "boundary" => boundary })
17
- self.content_length = parts.inject(0) {|sum,i| sum + i.length }
18
- self.body_stream = CompositeReadIO.new(*ios)
19
- end
8
+ require 'securerandom'
9
+
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.uuid}"
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
20
45
  end
46
+
47
+ attr :boundary
48
+ end