Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature request] Support: RFC 8941 Structured Field Values for HTTP #165

Open
karupanerura opened this issue Sep 28, 2021 · 3 comments
Open

Comments

@karupanerura
Copy link

refs. https://www.rfc-editor.org/rfc/rfc8941.html

@karupanerura
Copy link
Author

karupanerura commented Sep 28, 2021

For example, I could be happy if the following interface was provided:

use HTTP::Headers;
use HTTP::Headers::StructuredFieldValue qw/
    type_integer type_decimal type_string type_token type_bytes type_boolean
    type_list type_inner_list type_dictionary type_key
/;

my $headers = HTTP::Headers->new();

#
# setter interfaces
#
$headers->struct('Example-Integer', type_integer(42)); # => Example-Integer: 42
$headers->struct('Example-Decimal', type_decimal(4.5)); # => Example-Decimal: 4.5
$headers->struct('Example-String', type_string('hello world')); # => Example-String: "hello world"
$headers->struct('Example-Token', type_token('foo123/456')); # => Example-Token: foo123/456
$headers->struct('Example-ByteSequence', type_bytes('pretend this is binary content.')); # => Example-ByteSequence: :cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:
$headers->struct('Example-Boolean', type_boolean(1));# => Example-Boolean: ?1
# $headers->struct('Example-Boolean', !!1); # => Example-Boolean: ?1 (should it be OK? refs. https://github.com/Perl/perl5/pull/19040 ) 
$headers->struct('Example-Integer-WithParameters', type_integer(1)->with_parameters(type_key('a'), b => type_boolean(0))); # => Example-Integer-WithParameters: 1; a; b=?0 (NOTE: Parameters are an ordered map of key-value pairs)
$headers->struct('Example-List', type_list(
    type_inner_list(
        type_string('foo')->with_parameters(a => type_integer(1), b => type_integer(2))
    )->with_parameters(lvl => type_integer(5)),
    type_inner_list(type_string('bar'), type_string('baz'))->with_parameters(lvl => type_integer(1)),
)); # => Example-List: ("foo"; a=1;b=2);lvl=5, ("bar" "baz");lvl=1
$headers->struct('Example-Dict', type_dictionary(
    a => type_boolean(0),
    type_key('b'),
    type_key('c')->with_parameters(foo => type_token('bar')),
))# => Example-Dict: a=?0, b, c; foo=bar

#
# getter interfaces
#

# Example-Integer: 42
$headers->struct('Example-Integer')->value; # => 42
$headers->struct('Example-Integer')->type; # => "integer"

# Example-Decimal: 4.5
$headers->struct('Example-Decimal')->value; # => 4.5
$headers->struct('Example-Decimal')->type; # => "decimal"

# Example-String: "hello world"
$headers->struct('Example-String')->value; # => "hello world"
$headers->struct('Example-String')->type; # => "string"

# Example-Token: foo123/456
$headers->struct('Example-Token')->value; # => "foo123/456"
$headers->struct('Example-Token')->type; # => "token"

# Example-ByteSequence: :cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:
$headers->struct('Example-ByteSequence')->value; # => "pretend this is binary content."
$headers->struct('Example-ByteSequence')->type; # => "byte_sequence"

# Example-Boolean: ?1
$headers->struct('Example-Boolean')->value; # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Boolean')->type; # => "boolean"

# Example-Integer-WithParameters: 1; a; b=?0 (NOTE: Parameters are an ordered map of key-value pairs)
$headers->struct('Example-Integer-WithParameters')->value; # => 1
$headers->struct('Example-Integer-WithParameters')->type; # => "integer"
$headers->struct('Example-Integer-WithParameters')->has_parameters;  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Integer-WithParameters')->parameters->keys;  # =>("a", "b")
$headers->struct('Example-Integer-WithParameters')->parameters->length;  # => 2
$headers->struct('Example-Integer-WithParameters')->parameters->has_key('a'); # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Integer-WithParameters')->parameters->key('a')->has_item; # => !!0 (false/PL_sv_no)
$headers->struct('Example-Integer-WithParameters')->parameters->has_key('b'); # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Integer-WithParameters')->parameters->key('b')->has_item; # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Integer-WithParameters')->parameters->key('b')->item->value; # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Integer-WithParameters')->parameters->key('b')->item->type; # => "boolean"
$headers->struct('Example-Integer-WithParameters')->parameters->item('b')->value; # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Integer-WithParameters')->parameters->item('b')->type; # => "boolean"
$headers->struct('Example-Integer-WithParameters')->parameters->has_key('c'); # =>  !!0 (false/PL_sv_no)

# Example-List: ("foo"; a=1;b=2);lvl=5, ("bar" "baz");lvl=1
$headers->struct('Example-List')->type; # => "list"
$headers->struct('Example-List')->length; # => 2
$headers->struct('Example-List')->item(0)->type; # => "inner_list"
$headers->struct('Example-List')->item(0)->length; # => 1
$headers->struct('Example-List')->item(0)->item(0)->type; # => "string"
$headers->struct('Example-List')->item(0)->item(0)->value; # => "foo"
$headers->struct('Example-List')->item(0)->item(0)->has_parameters;  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-List')->item(0)->item(0)->parameters->item('a')->value; # => 1
$headers->struct('Example-List')->item(0)->item(0)->parameters->item('b')->value; # => 2
$headers->struct('Example-List')->item(0)->has_parameters;  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-List')->item(0)->parameters->item('lvl')->value; # => 5
$headers->struct('Example-List')->item(1)->type; # => "inner_list"
$headers->struct('Example-List')->item(1)->length; # => 2
$headers->struct('Example-List')->item(1)->item(0)->type; # => "string"
$headers->struct('Example-List')->item(1)->item(0)->value; # => "bar"
$headers->struct('Example-List')->item(1)->item(1)->type; # => "string"
$headers->struct('Example-List')->item(1)->item(1)->value; # => "baz"
$headers->struct('Example-List')->item(1)->has_parameters;  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-List')->item(1)->parameters->item('lvl')->value; # => 1

# Example-Dict: a=?0, b, c; foo=bar
$headers->struct('Example-Dict')->type; # => "dictionary"
$headers->struct('Example-Dict')->keys; # => ("a", "b", "c")
$headers->struct('Example-Dict')->length; # => 3
$headers->struct('Example-Dict')->has_key('a');  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Dict')->key('a')->has_item;  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Dict')->key('a')->has_parameters;  # => !!0 (false/PL_sv_no)
$headers->struct('Example-Dict')->key('a')->item->type; # => "boolean"
$headers->struct('Example-Dict')->key('a')->item->value; # => !!0 (false/PL_sv_no)
$headers->struct('Example-Dict')->item('a')->type; # => "boolean"
$headers->struct('Example-Dict')->item('a')->value; # => !!0 (false/PL_sv_no)
$headers->struct('Example-Dict')->has_key('b');  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Dict')->key('b')->has_item;  # => !!0 (false/PL_sv_no)
$headers->struct('Example-Dict')->key('b')->has_parameters;  # => !!0 (false/PL_sv_no)
$headers->struct('Example-Dict')->has_key('c');  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Dict')->key('c')->has_item;  # => !!0 (false/PL_sv_no)
$headers->struct('Example-Dict')->key('c')->has_parameters;  # => !!1 (true/PL_sv_yes)
$headers->struct('Example-Dict')->key('c')->parameters->item('foo')->type;  # => "token"
$headers->struct('Example-Dict')->key('c')->parameters->item('foo')->value;  # => "bar"
$headers->struct('Example-Dict')->has_key('d');  # => !!0 (false/PL_sv_no)

@karenetheridge
Copy link
Member

This looks like something that should go in its own distribution, say HTTP::Headers::WithStructuredFieldValues that adds read/write sugar via a subclass of HTTP::Headers. We would be reluctant to add a lot of code to HTTP::Headers directly that isn't expected to be heavily used or have wide-reaching value.

@vanHoesel
Copy link
Member

I have to agree with @karenetheridge about the fact that we shouldn't add too much inside HTTP::Headers.

The way I read it is that structured fields just another interesting way to add a HTTP header that has some special semantics on the string being used as its value. Parsing of that (large) string should ensure integrity of the data being passed, and either that field should be accepted, or rejected completely. It does not affect the processing of the other HTTP headers or its usage of the incoming or outgoing message itself.

Having said that, This wouldn't even require a full subclassing, just something that can handle that string.

@karupanerura , I'm willing to come up with a more easy Perl-like solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants