Archived
A tab icon you can actually see, and photo uploads that accept a photo
Two unrelated things the bakery hit on the same afternoon. THE FAVICON was not missing, it was unusable. The head pointed rel=icon at the 1000x1000 logo PNGs, so a browser fetched 71 KB to paint 16 square pixels, and the mark is a vine branch drawn in hairlines -- strokes thinner than one pixel at that size -- which arrives as a grey smudge. Replaced with a real icon set built from one leaf of that branch, filled rather than stroked, because at 16px a silhouette survives and an outline does not. A midrib was drawn first and cut the leaf into two pale slivers at tab size, so it went; the tilt, the two points and the stem carry the shape. The SVG answers prefers-color-scheme itself, which a .ico cannot, so the dark tab strip gets sage on bakery-900 instead of a glowing cream tile. The .ico is listed first on purpose: a browser takes the last format it understands, so reversing the two would hand Chrome the bitmap. PHOTO UPLOADS failed on anything over 1 MB, which is every photo a phone takes. The cause was an absence: nothing configured spring.servlet.multipart, so Boot's 1 MB default applied and the container rejected the file with FileSizeLimitExceededException before it reached ProductPhotoService -- the class whose entire job is turning "whatever came off a phone" into a resized webp. The pipeline could never run on the input it was written for. Now 15 MB a file and 60 MB a request, the latter because the file input is `multiple`. The failure was also ugly, and that is fixed separately: parsed eagerly, an over-sized part throws from inside Tomcat's parameter parsing where no @ExceptionHandler can reach it, so the request died as a 500 and then died again forwarding to /error, because that forward re-parsed the same too-large request (the paired "Exception Processing [ErrorPage...]" lines in the log). resolve-lazily moves the throw into argument binding, where AdminController now catches it and returns the same `problem` flash the domain's other refusals use. max-swallow-size lets the body be discarded so the browser receives that redirect rather than a connection reset. The multipart numbers are asserted rather than trusted, because a default that was never set is exactly the kind of thing that comes back silently.
This commit is contained in:
@@ -11,6 +11,23 @@ spring:
|
||||
open-in-view: false
|
||||
flyway:
|
||||
enabled: true
|
||||
servlet:
|
||||
multipart:
|
||||
# THIS BLOCK IS THE WHOLE REASON PHOTO UPLOADS FAILED. Unset, Boot defaults to a 1 MB max-file-size,
|
||||
# and a photo off a phone is 3-12 MB — so every real upload died with FileSizeLimitExceededException
|
||||
# before it reached ProductPhotoService, which exists precisely to resize "whatever came off a phone".
|
||||
# The resizing pipeline could never run on the input it was written for.
|
||||
max-file-size: 15MB
|
||||
# The file input is `multiple`, so one submit can carry several photos; this bounds the whole request
|
||||
# rather than each part. Four full-size photos at once is a realistic morning's worth of new stock.
|
||||
max-request-size: 60MB
|
||||
# Parse when the controller asks for the files, not while Tomcat is reading parameters. Eagerly, an
|
||||
# oversize part throws from inside the container's parameter parsing, which no @ExceptionHandler can
|
||||
# reach — the request dies as a 500 and then the forward to /error re-parses and throws again (the
|
||||
# "Exception Processing [ErrorPage...]" pairs in the log). Lazily, it surfaces as a
|
||||
# MaxUploadSizeExceededException during argument binding, where AdminController can catch it.
|
||||
resolve-lazily: true
|
||||
|
||||
mail:
|
||||
host: ${SMTP_SERVER:localhost}
|
||||
port: ${SMTP_PORT:25}
|
||||
@@ -29,6 +46,13 @@ spring:
|
||||
ssl:
|
||||
trust: ${SMTP_SERVER:localhost}
|
||||
|
||||
server:
|
||||
tomcat:
|
||||
# Read and discard the rest of an over-sized body instead of resetting the connection, so the browser
|
||||
# actually receives the redirect and the message rather than "connection reset". Only reachable now for
|
||||
# a genuinely enormous file, but that is exactly when a clear answer matters.
|
||||
max-swallow-size: -1
|
||||
|
||||
platform:
|
||||
web:
|
||||
spa:
|
||||
|
||||
Reference in New Issue
Block a user