Class: Protocol::Multipart::Parser::Part

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/multipart/parser.rb

Overview

Represents a single part within a multipart message.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(readable, headers, boundary) ⇒ Part

Initialize a new part with a readable stream, headers, and a boundary string.



20
21
22
23
24
25
26
# File 'lib/protocol/multipart/parser.rb', line 20

def initialize(readable, headers, boundary)
  @readable = readable
  @boundary = boundary
  @headers = headers
  @ended = false
  @is_closing = false
end

Instance Attribute Details

#headersObject (readonly)

Returns the value of attribute headers.



29
30
31
# File 'lib/protocol/multipart/parser.rb', line 29

def headers
  @headers
end

Instance Method Details

#closing_boundary?Boolean

Checks if this part ends with a closing boundary. A closing boundary indicates that this is the last part in the multipart message.

Returns:

  • (Boolean)


141
142
143
# File 'lib/protocol/multipart/parser.rb', line 141

def closing_boundary?
  @is_closing || (@readable.nil? && @is_closing)
end

#discardObject

Efficiently discards all data until the next boundary is found. This is used to skip parts without reading their content into memory.



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/protocol/multipart/parser.rb', line 118

def discard
  # Efficiently discard all data until boundary
  return unless @readable
  
  # Discard data until boundary
  @readable.discard_until("\r\n--#{@boundary}")
  
  self.read_boundary_suffix
  
  return nil
end

#each(chunk_size = 8192) ⇒ Object

Iterate through the part content in chunks.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/protocol/multipart/parser.rb', line 35

def each(chunk_size = 8192)
  return to_enum(:each, chunk_size) unless block_given?
  
  return unless @readable
  
  boundary_marker = "\r\n--#{@boundary}"
  
  # Stream data in chunks using read_until with a limit
  while @readable
    if chunk = @readable.read_until(boundary_marker, limit: chunk_size, chomp: true)
      # We found the boundary, check if it's a closing boundary:
      if suffix = @readable.read_until("\r\n", chomp: true)
        @is_closing = (suffix == "--")
        @ended = true
        @readable = nil
      else
        @readable = nil
        raise EOFError, "Unexpected end of stream while reading part data!"
      end
    else
      chunk = @readable.read(chunk_size)
    end
    
    if chunk
      yield chunk unless chunk.empty?
    else
      # No more data to read, break the loop:
      break
    end
  end
end

#ended?Boolean

Checks if this part has been completely read.

Returns:

  • (Boolean)


133
134
135
# File 'lib/protocol/multipart/parser.rb', line 133

def ended?
  @ended
end

#finishObject

Finishes reading this part’s data and advances to the next boundary.



103
104
105
106
107
108
109
110
111
112
# File 'lib/protocol/multipart/parser.rb', line 103

def finish
  return unless @readable
  
  # Read all data until boundary
  data = @readable.read_until("\r\n--#{@boundary}", chomp: true)
  
  self.read_boundary_suffix
  
  return data
end

#read_boundary_suffixObject

Reads the suffix after a boundary to determine if it’s a closing boundary.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/protocol/multipart/parser.rb', line 83

def read_boundary_suffix
  # Read the rest of the boundary line to check if it's closing:
  boundary_suffix = @readable.read(2)
  if boundary_suffix == "--"
    @is_closing = true
    @ended = true
    @readable = nil
  elsif boundary_suffix == "\r\n"
    @is_closing = false
    @ended = true
    @readable = nil
  else
    @readable = nil
    raise EOFError, "Unexpected end of stream while reading part data!"
  end
end

#read_empty_boundary?Boolean

Checks if the next content is an empty boundary (part with no content).

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
# File 'lib/protocol/multipart/parser.rb', line 70

def read_empty_boundary?
  boundary_marker = "--#{@boundary}"
  if @readable.peek(boundary_marker.bytesize) == boundary_marker
    @readable.read(boundary_marker.bytesize)
    self.read_boundary_suffix
    
    return true
  end
  
  return false
end

#The headers associated with this part.=(headersassociatedwiththispart. = (value)) ⇒ Object



29
# File 'lib/protocol/multipart/parser.rb', line 29

attr_reader :headers