Getting Started
This guide explains how to parse media-typed content using built-in and custom parsers.
Installation
Add the gem to your project:
$ bundle add protocol-content
Parse Content
A class Protocol::Content::Parser selects an interpretation according to the media type:
require "protocol/content"
require "json"
parser = Protocol::Content::Parser.build do |parser|
parser.register("application/json") do |input|
JSON.parse(input.read)
end
end
value = File.open("document.json") do |input|
parser.parse("application/json", input)
end
The caller owns media-type extraction and adapts the encoded body to a readable IO-like input. This keeps parsing independent of request, response, and transport abstractions. Registered handlers receive the readable input and parsed Protocol::Media::Type. Applications decide whether and where parsed values should be memoized.
Parse Protocol HTTP Bodies
A Protocol::HTTP::Body::Readable can be adapted to a readable stream using #to_io. The caller must close that stream after parsing, passing through any parser error:
media_type = request.headers["content-type"]
input = request.body.to_io
begin
value = parser.parse(media_type, input)
rescue => error
raise
ensure
input.close_read(error)
end
This example specifically assumes a Protocol::HTTP body. The stream's #close_read method releases its input buffer and invokes #close(error) on the underlying body. Generic IO-like parser inputs do not necessarily provide this interface.
Default Parsers
The default parser supports JSON, URL-encoded forms, and multipart forms with bounded defaults:
require "protocol/content/default"
value = Protocol::Content::Parser.default.parse(
media_type,
input,
)
The format libraries are included as dependencies, so these defaults are available from a normal installation.
Configure Limits
Format parsers can be configured explicitly for endpoint-specific limits:
require "protocol/content/json_parser"
json_parser = Protocol::Content::JSONParser.new(
size_limit: 4 * 1024 * 1024,
depth_limit: 32,
)
parser = Protocol::Content::Parser.build do |parser|
parser.register("application/json") do |input|
json_parser.parse(input)
end
end
Limits are inclusive. Content at the configured limit is accepted, while content exceeding it raises class Protocol::Content::ContentTooLargeError.
Stream Multipart Uploads
Multipart fields can be collected into Protocol::URL::FormData::Nested while file uploads are streamed to application-managed storage. The value returned by the block is assigned to the nested result:
require "protocol/content/default"
form_data = Protocol::Content::Parser.default.parse(media_type, input) do |name, value|
case value
when Protocol::Multipart::FormData::Upload
upload = uploads.create(name, value.filename, value.headers)
value.each do |chunk|
upload.write(chunk)
end
upload
when String
value
end
end
Here, uploads represents an application storage interface. The parser yields a String for each buffered field and a Protocol::Multipart::FormData::Upload for each file upload. The value returned by the block is stored in the nested result.
An upload can only be read during its block invocation. After the block returns, the parser consumes and discards any unread upload bytes while continuing to enforce the upload and total size limits. Applications must therefore consume uploads within the block when they need to persist their contents.