Module: Sentiero::Web::BodyReader
- Defined in:
- lib/sentiero/web/body_reader.rb
Overview
Reads a request body (optionally gzip-encoded) with a hard cap (default 512KB, config.max_body_size) on BOTH the compressed and decompressed size, so a gzip bomb can't blow past it. Shared by the two untrusted-input lanes (EventsApp, IngestApp), which differ only in the response they build from the error symbol.
Constant Summary collapse
- MAX_BODY_SIZE =
512 KB
524_288- ERRORS =
error symbol => [http_status, message]
{ too_large: [413, "request body too large"], bad_gzip: [400, "invalid gzip encoding"] }.freeze
Class Method Summary collapse
-
.gzip?(env, raw) ⇒ Boolean
Decompress when the client declared gzip or the body starts with the gzip magic number.
-
.read(env, max_bytes: MAX_BODY_SIZE) ⇒ Object
[body, nil] on success, or [nil, :too_large | :bad_gzip] on failure.
Class Method Details
.gzip?(env, raw) ⇒ Boolean
Decompress when the client declared gzip or the body starts with the gzip magic number. The magic-byte path lets unload beacons ship compressed as text/plain without a Content-Encoding header, which would otherwise force a CORS preflight sendBeacon can't perform. JSON never begins with these bytes, so detection is unambiguous.
49 50 51 52 |
# File 'lib/sentiero/web/body_reader.rb', line 49 def gzip?(env, raw) env["HTTP_CONTENT_ENCODING"]&.downcase == "gzip" || (raw.getbyte(0) == 0x1f && raw.getbyte(1) == 0x8b) end |
.read(env, max_bytes: MAX_BODY_SIZE) ⇒ Object
[body, nil] on success, or [nil, :too_large | :bad_gzip] on failure.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/sentiero/web/body_reader.rb', line 25 def read(env, max_bytes: MAX_BODY_SIZE) raw = env["rack.input"].read(max_bytes + 1) || "" env["rack.input"].rewind if env["rack.input"].respond_to?(:rewind) return [nil, :too_large] if raw.bytesize > max_bytes if gzip?(env, raw) begin gz = Zlib::GzipReader.new(StringIO.new(raw)) raw = gz.read(max_bytes + 1) || "" gz.close rescue Zlib::GzipFile::Error return [nil, :bad_gzip] end return [nil, :too_large] if raw.bytesize > max_bytes end [raw, nil] end |