Module: Philiprehberger::HttpClient::Multipart

Defined in:
lib/philiprehberger/http_client/multipart.rb

Overview

Builds multipart/form-data request bodies from a hash of fields. Supports both string values and File/IO objects.

Constant Summary collapse

CRLF =
"\r\n"

Class Method Summary collapse

Class Method Details

.build(fields) ⇒ Array(String, String)

Build a multipart/form-data body and content-type header.

Parameters:

  • fields (Hash)

    field name => value pairs (String or File/IO)

Returns:

  • (Array(String, String))

    the body string and content-type header value



16
17
18
19
20
21
# File 'lib/philiprehberger/http_client/multipart.rb', line 16

def self.build(fields)
  boundary = generate_boundary
  body = build_body(fields, boundary)
  content_type = "multipart/form-data; boundary=#{boundary}"
  [body, content_type]
end

.build_body(fields, boundary) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



29
30
31
32
# File 'lib/philiprehberger/http_client/multipart.rb', line 29

def self.build_body(fields, boundary)
  parts = fields.map { |name, value| build_part(name, value, boundary) }
  parts.join + "--#{boundary}--#{CRLF}"
end

.build_field_part(name, value, boundary) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



58
59
60
61
62
63
64
65
66
# File 'lib/philiprehberger/http_client/multipart.rb', line 58

def self.build_field_part(name, value, boundary)
  ''.dup.tap do |part|
    part << "--#{boundary}#{CRLF}"
    part << "Content-Disposition: form-data; name=\"#{name}\"#{CRLF}"
    part << CRLF
    part << value.to_s
    part << CRLF
  end
end

.build_file_part(name, file, boundary) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



44
45
46
47
48
49
# File 'lib/philiprehberger/http_client/multipart.rb', line 44

def self.build_file_part(name, file, boundary)
  filename = file.respond_to?(:path) ? File.basename(file.path) : 'upload'
  content = read_file_content(file)
  disposition = "Content-Disposition: form-data; name=\"#{name}\"; filename=\"#{filename}\"#{CRLF}"
  "--#{boundary}#{CRLF}#{disposition}Content-Type: application/octet-stream#{CRLF}#{CRLF}#{content}#{CRLF}"
end

.build_part(name, value, boundary) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



35
36
37
38
39
40
41
# File 'lib/philiprehberger/http_client/multipart.rb', line 35

def self.build_part(name, value, boundary)
  if value.respond_to?(:read)
    build_file_part(name, value, boundary)
  else
    build_field_part(name, value, boundary)
  end
end

.generate_boundaryObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



24
25
26
# File 'lib/philiprehberger/http_client/multipart.rb', line 24

def self.generate_boundary
  "----RubyFormBoundary#{SecureRandom.hex(16)}"
end

.read_file_content(file) ⇒ Object



51
52
53
54
55
# File 'lib/philiprehberger/http_client/multipart.rb', line 51

def self.read_file_content(file)
  content = file.read
  file.rewind if file.respond_to?(:rewind)
  content
end