Starry
Starry is a Ruby library for HTTP Structured Field Values (RFC 8941).
Install
Add the following line to your Gemfile
:
gem "starry"
Or just gem install starry
.
Basic Usage
Parsing
Use an appropriate one of Starry.parse_list
, Starry.parse_dictionary
, or Starry.parse_item
.
require 'starry'
# Basic case: Proxy-Status field (RFC 9209)
Starry.parse_list('r34.example.net; error=http_request_error, ExampleCDN')
# =>
# [#<Starry::Item:0x0000000105a82658
# @parameters={"error"=>:http_request_error},
# @value=:"r34.example.net">,
# #<Starry::Item:0x0000000105a81cd0 @parameters={}, @value=:ExampleCDN>]
# Retrieving bare value of item
Starry.parse_list('r34.example.net; error=http_request_error, ExampleCDN')[1].value
# => :ExampleCDN
# Retrieving parameters hash of item
Starry.parse_list('r34.example.net; error=http_request_error, ExampleCDN')[0].parameters
# => {"error"=>:http_request_error}
# Using `symbolize_names` option
Starry.parse_list('r34.example.net; error=http_request_error, ExampleCDN', symbolize_names: true)[0].parameters
# => {:error=>:http_request_error}
# More complex case: Signature-Input field (draft-ietf-httpbis-message-signatures)
Starry.parse_dictionary(
'sig1=("@method" "@authority" "@path" "content-digest" "content-type" ' \
'"content-length");created=1618884475;keyid="test-key-ecc-p256", ' \
'proxy_sig=("@method" "@authority" "@path" "content-digest" "content-type" ' \
'"content-length" "forwarded");created=1618884480;keyid="test-key-rsa";' \
'alg="rsa-v1_5-sha256";expires=1618884540'
)
# =>
# {"sig1"=>
# #<Starry::InnerList:0x0000000107dc8c70
# @parameters={"created"=>1618884475, "keyid"=>"test-key-ecc-p256"},
# @value=
# [#<Starry::Item:0x0000000107dcab88 @parameters={}, @value="@method">,
# ...,
# #<Starry::Item:0x0000000107dc9648
# @parameters={},
# @value="content-length">]>,
# "proxy_sig"=>
# #<Starry::InnerList:0x0000000107dc5a98
# @parameters=
# {"created"=>1618884480,
# "keyid"=>"test-key-rsa",
# "alg"=>"rsa-v1_5-sha256",
# "expires"=>1618884540},
# @value=
# [#<Starry::Item:0x0000000107dc8450 @parameters={}, @value="@method">,
# ...,
# #<Starry::Item:0x0000000107dc6b00 @parameters={}, @value="forwarded">]>}
Serializing
Use Starry.serialize
. This single method is used for all three types: List, Dictionary, and Item.
If an Item or an Inner List has parameters, create a new Starry::Item
or Starry::InnerList
class object and use it. Their constructor takes a value as its first argument and a parameters hash as its second argument. Keys of the parameters hash can be either strings or symbols. Note that non-parameterized values can be contained without such a wrapping object.
require 'starry'
Starry.serialize([
Starry::Item.new(:'r34.example.net', { 'error' => :http_request_error }),
:ExampleCDN
])
# => "r34.example.net;error=http_request_error, ExampleCDN"
Starry.serialize({
'sig-b22' => Starry::InnerList.new(
['@authority', 'content-digest', Starry::Item.new('@query-param', { name: 'Pet' })],
{ created: 1618884473, keyid: 'test-key-rsa-pss', tag: 'header-example' }
)
})
# => "sig-b22=(\"@authority\" \"content-digest\" \"@query-param\";name=\"Pet\");created=1618884473;keyid=\"test-key-rsa-pss\";tag=\"header-example\""
Further Topics
Type mappings
The Structured Field Values specification defines several data types. Here we describe which Ruby class is used for each of them in this library.
- For Lists,
Array
is used. - For Inner Lists,
Starry::InnerList
is used.- If it is not parameterized,
Array
may also be used when serializing.
- If it is not parameterized,
- For parameters,
Hash
is used.- Keys can be either
String
orSymbol
. When parsing, the symbolize_names option indicates which is to be used. - When serializing, be aware of the syntax; see the RFC for details.
- Keys can be either
- For Dictionaries,
Hash
is used. - For Items,
Starry::Item
is used.- When serializing,
Starry::Item
is not necessarily needed if the value is not parameterized. See the description and examples above.
- When serializing,
- For Integer values,
Integer
is used.- Note that its absolute value is limited to less than
10**15
.
- Note that its absolute value is limited to less than
- For Decimal values,
Float
is used.- Note that its absolute value is limited to less than
10**12
. Also, it is rounded to three decimal places when serialized.
- Note that its absolute value is limited to less than
- For String values,
String
with encoding other thanASCII_8BIT
is used.- Note that only ASCII printable characters are allowed.
- For Token values,
Symbol
is used.- Be aware of the syntax when serializing; see the RFC for details.
- For Byte Sequence (binary) values,
String
with encodingASCII_8BIT
is used. - For Boolean values,
true
andfalse
are used.
Pattern matching
Starry::Item
and Starry::InnerList
support pattern matching.
require 'starry'
# `Starry::Item` can be matched with hash pattern using `value` and `parameters` keys.
case Starry.parse_item('r34.example.net; error=http_request_error')
in value:, parameters:
"v: #{ value }, p: #{ parameters }"
else
raise "not matched"
end
# => "v: r34.example.net, p: {\"error\"=>:http_request_error}"
# The same applies to `Starry::InnerList`.
inner_list = Starry.parse_dictionary(
'sig-b25=("date" "@authority" "content-type");created=1618884473;' \
'keyid="test-shared-secret"'
)['sig-b25']
case inner_list
in value:, parameters:
"v: #{ value.map(&:value) }, p: #{ parameters }"
else
raise "not matched"
end
# => "v: [\"date\", \"@authority\", \"content-type\"], p: {\"created\"=>1618884473, \"keyid\"=>\"test-shared-secret\"}"
# `Starry::InnerList` can be also matched with array pattern.
# Note that parameters cannot be available for matching in array pattern.
case inner_list
in first, second, *rest
"first: #{ first.value }, second: #{ second.value }, rest: #{ rest.map(&:value) }"
else
raise "not matched"
end
# => "first: date, second: @authority, rest: [\"content-type\"]"