Skip to content

Commit 70f0e19

Browse files
committed
✨ Support LITERAL+ and LITERAL- capabilities
This also adds a new config attribute: `max_non_synchronizing_literal`. By default, it is set rather conservatively to 16 KiB.
1 parent b0cfc82 commit 70f0e19

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

lib/net/imap.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,16 +450,16 @@ module Net
450450
#
451451
# Although IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051] is not supported
452452
# yet, Net::IMAP supports several extensions that have been folded into it:
453-
# +ENABLE+, +IDLE+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+, +UNSELECT+,
454-
# <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
453+
# +ENABLE+, +IDLE+, +LITERAL-+, +MOVE+, +NAMESPACE+, +SASL-IR+, +UIDPLUS+,
454+
# +UNSELECT+, <tt>STATUS=SIZE</tt>, and the fetch side of +BINARY+.
455455
# Commands for these extensions are listed with the {Core IMAP
456456
# commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands], above.
457457
#
458458
# >>>
459459
# <em>The following are folded into +IMAP4rev2+ but are currently
460460
# unsupported or incompletely supported by</em> Net::IMAP<em>: RFC4466
461461
# extensions, +SEARCHRES+, +LIST-EXTENDED+, +LIST-STATUS+,
462-
# +LITERAL-+, and +SPECIAL-USE+.</em>
462+
# and +SPECIAL-USE+.</em>
463463
#
464464
# ==== RFC2087: +QUOTA+
465465
# +NOTE:+ Only the +STORAGE+ quota resource type is currently supported.
@@ -569,6 +569,15 @@ module Net
569569
# - Updates #store and #uid_store with the +unchangedsince+ modifier and adds
570570
# the +MODIFIED+ ResponseCode to the tagged response.
571571
#
572+
# ==== RFC7888: <tt>LITERAL+</tt>
573+
# - Literal strings smaller than Config#max_non_synchronizing_literal bytes
574+
# are sent without waiting for the server's continuation request.
575+
#
576+
# ==== RFC7888: +LITERAL-+
577+
# - Literal strings smaller than 4096 bytes or
578+
# Config#max_non_synchronizing_literal (whichever is smaller)
579+
# are sent without waiting for the server's continuation request.
580+
#
572581
# ==== RFC8438: <tt>STATUS=SIZE</tt>
573582
# - Updates #status with the +SIZE+ status attribute.
574583
#

lib/net/imap/command_data.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ def send_binary_literal(*, **) = send_literal(*, **, binary: true)
8484
# TODO: raise or warn when capabilities don't allow non_sync.
8585
# * `false` -> Force normal synchronizing literal behavior.
8686
# * `nil` -> (default) Currently behaves like `false` (will be dynamic).
87-
# TODO: Dynamic, based on capabilities and bytesize.
8887
def send_literal(str, tag = nil, binary: false, non_sync: nil)
8988
synchronize do
89+
non_sync = non_sync_literal?(str.bytesize) if non_sync.nil?
9090
prefix = "~" if binary
9191
plus = "+" if non_sync
9292
put_string("#{prefix}{#{str.bytesize}#{plus}}\r\n")
@@ -108,6 +108,13 @@ def send_literal(str, tag = nil, binary: false, non_sync: nil)
108108
end
109109
end
110110

111+
def non_sync_literal?(bytesize)
112+
capabilities_cached? &&
113+
bytesize <= config.max_non_synchronizing_literal &&
114+
(capable?("LITERAL+") ||
115+
bytesize <= 4096 && (capable?("IMAP4rev2") || capable?("LITERAL-")))
116+
end
117+
111118
def send_number_data(num)
112119
put_string(num.to_s)
113120
end

lib/net/imap/config.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,36 @@ def self.[](config)
281281
0.5r => true,
282282
}
283283

284+
# The maximum bytesize for sending non-synchronizing literals, to servers
285+
# which support them. To disable non-synchronizing literals, set the
286+
# value to +-1+.
287+
#
288+
# Non-synchronizing literals are only sent when the server's capabilities
289+
# have been already been cached and include either
290+
# <tt>LITERAL+</tt> [RFC7888[https://www.rfc-editor.org/rfc/rfc7888]],
291+
# +LITERAL-+ [RFC7888[https://www.rfc-editor.org/rfc/rfc7888]], or
292+
# +IMAP4rev2+ [RFC9051[https://www.rfc-editor.org/rfc/rfc9051]].
293+
#
294+
# For <tt>LITERAL+</tt>, this value is the only limit on non-synchronizing
295+
# literals. Otherwise, literals must be smaller than 4096 bytes.
296+
#
297+
# Non-synchronizing literals avoid the latency of waiting for the server
298+
# to allow continuation. However, if a client sends a non-synchronizing
299+
# literal that is too large for the server, the server's only recourse is
300+
# to close the connection. Because <tt>LITERAL+</tt> gives no indication
301+
# of the server's limits, it's best to avoid sending very large literals.
302+
#
303+
# ==== Versioned Defaults
304+
#
305+
# Net::IMAP#max_non_synchronizing_literal <em>was added in +v0.6.4+.</em>
306+
#
307+
# * original: +-1+ <em>(never send non-synchronizing literals)</em>
308+
# * +0.6+: 16 KiB
309+
attr_accessor :max_non_synchronizing_literal, type: Integer?, defaults: {
310+
0.0r => 0,
311+
0.6r => 16 << 16, # 16 KiB
312+
}
313+
284314
# The maximum allowed server response size. When +nil+, there is no limit
285315
# on response size.
286316
#

0 commit comments

Comments
 (0)