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

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

Overview

Represents a single part within a multipart message.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Readable

#copy_to, #save

Constructor Details

#initialize(readable, headers, boundary) ⇒ Part

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



40
41
42
43
44
45
46
# File 'lib/protocol/multipart/parser.rb', line 40

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.



49
50
51
# File 'lib/protocol/multipart/parser.rb', line 49

def headers
  @headers
end

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



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

attr_reader :headers

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)


161
162
163
# File 'lib/protocol/multipart/parser.rb', line 161

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.



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/protocol/multipart/parser.rb', line 138

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.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/protocol/multipart/parser.rb', line 55

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)


153
154
155
# File 'lib/protocol/multipart/parser.rb', line 153

def ended?
	@ended
end

#finishObject

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



123
124
125
126
127
128
129
130
131
132
# File 'lib/protocol/multipart/parser.rb', line 123

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.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/protocol/multipart/parser.rb', line 103

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)


90
91
92
93
94
95
96
97
98
99
100
# File 'lib/protocol/multipart/parser.rb', line 90

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