Class: Candid::Internal::Multipart::Encoder Private

Inherits:
Object
  • Object
show all
Defined in:
lib/candid/internal/multipart/multipart_encoder.rb

Overview

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

Encodes parameters into a ‘multipart/form-data` payload as described by RFC 2388:

https://tools.ietf.org/html/rfc2388

This is most useful for transferring file-like objects.

Parameters should be added with ‘#encode`. When ready, use `#body` to get the encoded result and `#content_type` to get the value that should be placed in the `Content-Type` header of a subsequent request (which includes a boundary value).

This abstraction is heavily inspired by Stripe’s multipart/form-data implementation, which can be found here:

https://github.com/stripe/stripe-ruby/blob/ca00b676f04ac421cf5cb5ff0325f243651677b6/lib/stripe/multipart_encoder.rb#L18

Constant Summary collapse

CONTENT_TYPE =

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

"multipart/form-data"
CRLF =

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

"\r\n"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEncoder

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.

Returns a new instance of Encoder.



30
31
32
33
34
35
36
37
38
39
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 30

def initialize
  # Chose the same number of random bytes that Go uses in its standard
  # library implementation. Easily enough entropy to ensure that it won't
  # be present in a file we're sending.
  @boundary = SecureRandom.hex(30)

  @body = String.new
  @closed = false
  @first_field = true
end

Instance Attribute Details

#bodyObject (readonly)

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.



28
29
30
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 28

def body
  @body
end

#boundaryObject (readonly)

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.



28
29
30
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 28

def boundary
  @boundary
end

Instance Method Details

#closenil

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.

Finalizes the encoder by writing the final boundary.

Returns:

  • (nil)


119
120
121
122
123
124
125
126
127
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 119

def close
  raise "Encoder already closed" if @closed

  @body << CRLF
  @body << "--#{@boundary}--"
  @closed = true

  nil
end

#content_typeString

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.

Gets the content type string including the boundary.

Returns:

  • (String)

    The content type with boundary



44
45
46
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 44

def content_type
  "#{CONTENT_TYPE}; boundary=#{@boundary}"
end

#encode(form_data) ⇒ String

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.

Encode the given FormData object into a multipart/form-data payload.

Parameters:

  • form_data (FormData)

    The form data to encode

Returns:

  • (String)

    The encoded body.



52
53
54
55
56
57
58
59
60
61
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 52

def encode(form_data)
  return "" if form_data.parts.empty?

  form_data.parts.each do |part|
    write_part(part)
  end
  close

  @body
end

#write_field(name:, data:, filename: nil, headers: nil) ⇒ nil

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.

Writes a field to the encoder.

Parameters:

  • name (String)

    The field name

  • data (String)

    The field data

  • filename (String, nil) (defaults to: nil)

    Optional filename

  • headers (Hash<String, String>, nil) (defaults to: nil)

    Optional additional headers

Returns:

  • (nil)


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 87

def write_field(name:, data:, filename: nil, headers: nil)
  raise "Cannot write to closed encoder" if @closed

  if @first_field
    @first_field = false
  else
    @body << CRLF
  end

  @body << "--#{@boundary}#{CRLF}"
  @body << %(Content-Disposition: form-data; name="#{escape(name.to_s)}")
  @body << %(; filename="#{escape(filename)}") if filename
  @body << CRLF

  if headers
    headers.each do |key, value|
      @body << "#{key}: #{value}#{CRLF}"
    end
  elsif filename
    # Default content type for files.
    @body << "Content-Type: application/octet-stream#{CRLF}"
  end

  @body << CRLF
  @body << data.to_s

  nil
end

#write_part(part) ⇒ nil

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.

Writes a FormDataPart to the encoder.

Parameters:

Returns:

  • (nil)


67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/candid/internal/multipart/multipart_encoder.rb', line 67

def write_part(part)
  raise "Cannot write to closed encoder" if @closed

  write_field(
    name: part.name,
    data: part.contents,
    filename: part.filename,
    headers: part.headers
  )

  nil
end