web: leave the 204 PUT response bodyless
A route block returning "" gets it written into the body, so the 204
carried text/html and content-length headers that Rack 3 lint (rackup
under rake dev) rejects on a bodyless status.
Assisted-by: GLM-5.3 via pi
diff --git a/lib/pro_tacts/web.rb b/lib/pro_tacts/web.rb
index dd93a44..fb182e1 100644
--- a/lib/pro_tacts/web.rb
+++ b/lib/pro_tacts/web.rb
@@ -382,7 +382,7 @@ module ProTacts
# the request's own validity first, the conditionals on stored
# state after — so a card that cannot be stored hears
# CARDDAV:valid-address-data even when its If-Match is stale too.
- #: (String id) -> String
+ #: (String id) -> String?
def write_card(id)
# The body is the one binary input: Rack requires input in
# ASCII-8BIT and Rack::RewindableInput enforces it again. Relabel
@@ -438,7 +438,11 @@ module ProTacts
# contributes properties at write time, that PUT must stop
# sending one.
response["ETag"] = stored.etag
- ""
+
+ # A returned "" would land in the body and pin text/html and
+ # content-length onto the 204, which a bodyless status must not
+ # carry (Rack 3's lint rejects both); nil leaves it bodyless.
+ nil
end
# A parse that answers nil rather than raising when the bytes are
diff --git a/test/pro_tacts/test_web.rb b/test/pro_tacts/test_web.rb
index d90400c..00f9345 100644
--- a/test/pro_tacts/test_web.rb
+++ b/test/pro_tacts/test_web.rb
@@ -218,6 +218,11 @@ class WebTest < Minitest::Test
etag = %("#{Digest::SHA256.hexdigest(updated)}")
assert_equal etag, last_response["ETag"]
+ # A bodyless status must carry neither header: Rack 3's lint rejects
+ # both on a 204, and `rake dev` runs under it.
+ assert_nil last_response["Content-Type"]
+ assert_nil last_response["Content-Length"]
+
# The tag the PUT returned is the one every read now reports, which
# is what lets a client keep syncing against its own write.
get "/dav/addressbook/aiden.vcf"