-
Notifications
You must be signed in to change notification settings - Fork 40
preliminary minimal changes #251
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
base: main
Are you sure you want to change the base?
Changes from all commits
1e9da7b
5662117
169f4ee
b7af141
8ea1649
842cf81
014bfd0
4a289b6
37f292f
5e802ad
bfc08bc
01ce9fa
85bb7c8
f94507c
5e565d2
e7fbc7b
4120d59
ae92947
bb8c049
4a598f3
851eff9
a3a177f
9f09027
d18d284
c725a7f
ed51d78
9a38dca
d3a56b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like a strange design to only have special http header objects for one type of header. Is there any reason this needs its own object? The motivation for it isn't clear to me as the commit message on this file is just "initial strict implementation".
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ETags can be tagged as weak validators and might be handled differently in those cases. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| use HTTP::Header::Field; | ||
|
|
||
| unit class HTTP::Header::ETag is HTTP::Header::Field; | ||
|
|
||
| has Bool:D $.weak is required; | ||
|
|
||
| method new ( $value, Bool :$weak ) { | ||
| self.bless: | ||
| name => 'ETag', | ||
| :$weak, | ||
| values => $value | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ use HTTP::Header; | |
| use HTTP::MediaType; | ||
| use Encode; | ||
|
|
||
| has Bool $.strict is rw; | ||
| has HTTP::Header $.header = HTTP::Header.new; | ||
| has $.content is rw; | ||
|
|
||
|
|
@@ -12,12 +13,20 @@ has $.protocol is rw = 'HTTP/1.1'; | |
| has Bool $.binary = False; | ||
| has Str @.text-types; | ||
|
|
||
| my $CRLF = "\r\n"; | ||
| my constant $CRLF = "\x[0d]\x[0a]"; | ||
| my constant $DELIM = $CRLF x 2; | ||
| my constant $STRICT = True; # prepare for assoc. strict to positional strict | ||
|
|
||
| method new($content?, *%fields) { | ||
| my $header = HTTP::Header.new(|%fields); | ||
| multi method new($content, Bool $strict = False, *%fields) { | ||
| my $header = HTTP::Header.new($strict, |%fields); | ||
|
|
||
| self.bless(:$header, :$content); | ||
| self.bless(:$header, :$content, :$strict); | ||
| } | ||
|
|
||
| multi method new(Bool $strict = False, *%fields) is default { | ||
| my $header = HTTP::Header.new($strict, |%fields); | ||
|
|
||
| self.bless(:$header, :$strict); | ||
| } | ||
|
|
||
| method add-content($content) { | ||
|
|
@@ -103,6 +112,17 @@ method is-text(--> Bool:D) { | |
|
|
||
| method is-binary(--> Bool:D) { !self.is-text } | ||
|
|
||
| #| multiple transfer-codings can be listed; chunked should be last | ||
| #| https://datatracker.ietf.org/doc/html/rfc2616#section-14.41 | ||
| #| https://datatracker.ietf.org/doc/html/rfc7230#section-4 | ||
| multi method is-chunked ( HTTP::Header $header --> Bool:D ) { | ||
| my $enc = $header.field('Transfer-Encoding'); | ||
| so $enc and $enc.values.tail.trim.lc.ends-with: 'chunked' | ||
| } | ||
| multi method is-chunked(--> Bool:D) { | ||
| self.is-chunked: $!header; | ||
| } | ||
|
|
||
| method content-encoding() { | ||
| $!header.field('Content-Encoding'); | ||
| } | ||
|
|
@@ -180,48 +200,87 @@ method clear { | |
| $.content = '' | ||
| } | ||
|
|
||
| method parse($raw_message) { | ||
| my @lines = $raw_message.split(/$CRLF/); | ||
|
|
||
| my ($first, $second, $third) = @lines.shift.split(/\s+/); | ||
| # parsing content with embedded CRLFs NYI | ||
| # it would require taking encoding into account and working with Blobs | ||
| method !parse-content-strict ( $content ) { | ||
| if self.is-chunked { | ||
| # technically incorrect - content allowed to contain embedded CRLFs | ||
| my @lines = $content.split: $CRLF; | ||
| # pop zero-length Str that occurs after last chunk | ||
| # what to do if this doesn't happen? | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know, what should this do?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One option would be: if the user specified throw-exceptions, to throw with 'truncated last chunk'. The changes are meant to focus on producing strict output rather than demand strict input, so I'm not sure about this - your thoughts? |
||
| @lines.pop if @lines %2; | ||
| @lines = grep so *, | ||
| @lines.map: | ||
| -> $d, $s { $d ~~ /^<[0..9]>/ ?? $s !! Str } | ||
| ; | ||
| $.content = @lines.join; | ||
| } else { | ||
| $.content = $content; | ||
| } | ||
| } | ||
|
|
||
| method !parse-first ($raw_message, --> Str) { | ||
| my ( $start-line, $rest ) = $raw_message.split: $CRLF, 2; | ||
| my ($first, $second, $third) = $start-line.split(/\s+/); | ||
| if $third.index('/') { # is a request | ||
| $.protocol = $third; | ||
| } | ||
| else { # is a response | ||
| $.protocol = $first; | ||
| } | ||
| $rest; | ||
| } | ||
|
|
||
| loop { | ||
| last until @lines; | ||
|
|
||
| my $line = @lines.shift; | ||
| if $line { | ||
| my ($k, $v) = $line.split(/\:\s*/, 2); | ||
| if $k and $v { | ||
| if $.header.field($k) { | ||
| $.header.push-field: |($k => $v.split(',')>>.trim); | ||
| } else { | ||
| $.header.field: |($k => $v.split(',')>>.trim); | ||
| } | ||
| method !parse-header($header) { | ||
| my @lines = $header.split($CRLF); | ||
| for @lines -> $line { | ||
| my ($k, $v) = $line.split(/\:\s*/, 2); | ||
| if $k and $v { | ||
| if $!header.field($k) { | ||
| $!header.push-field: |($k => $v.split(',')>>.trim); | ||
| } else { | ||
| $!header.field: |($k => $v.split(',')>>.trim); | ||
| } | ||
| } else { | ||
| $.content = @lines.grep({ $_ }).join("\n"); | ||
| last; | ||
| } | ||
| # else warn? | ||
| } | ||
| } | ||
|
|
||
| method !parse-header-strict($header) { | ||
| $!header.parse($header, $STRICT); | ||
| } | ||
|
|
||
| method parse($raw_message, Bool $strict is copy = False) { | ||
| $strict ||= $!strict; | ||
| my $rest = self!parse-first($raw_message); | ||
| my ($header, $content) = $rest.split($DELIM, 2); | ||
| if $strict { | ||
| $!header.parse($header, $STRICT); | ||
| self!parse-content-strict($content) if $content; | ||
| } else { | ||
| self!parse-header($header); | ||
| $!content = join "\n", grep so *, $content.split: $CRLF; | ||
| } | ||
| self | ||
| } | ||
|
|
||
| method Str($eol = "\n", :$debug, Bool :$bin) { | ||
| multi method Str(Str $eol is copy = "\n", Bool $strict is copy = False, :$debug, Bool :$bin) { | ||
| $strict ||= $!strict; | ||
| $eol = $CRLF if $strict; | ||
|
|
||
| my constant $max_size = 300; | ||
| my $s = $.header.Str($eol); | ||
| $s ~= $eol if $.content; | ||
| self.field: Content-Length => ( $!content.?encode or $!content ).bytes.Str | ||
| if $strict and $!content and not self.is-chunked; | ||
| my $s = $.header.Str($eol, $strict); | ||
| $s ~= $eol if $!content and not $strict; | ||
|
|
||
| # The :bin will be passed from the H::UA | ||
| if not $bin { | ||
| $s ~= $.content ~ $eol if $.content and !$debug; | ||
| if $strict { | ||
| $s ~= $CRLF ~ ( $.content || '' ); | ||
| } else { | ||
| $s ~= $.content ~ $eol if $.content and !$debug; | ||
| } | ||
| } | ||
| if $.content and $debug { | ||
| if $bin || self.is-binary { | ||
|
|
@@ -237,5 +296,8 @@ method Str($eol = "\n", :$debug, Bool :$bin) { | |
|
|
||
| $s | ||
| } | ||
| multi method Str(Bool $strict is copy = False, :$debug, Bool :$bin) { | ||
| self.Str: "\n", $strict, :$debug, :$bin; | ||
| } | ||
|
|
||
| # vim: expandtab shiftwidth=4 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other grammar in this file is called
HTTP::Header::Grammarwhich makes it clear what it is used for. To me a name likeGrammar::Strictin the same file does not suggest it has any relation toHTTP::Header::Grammareven though it appears that is the case based on theparsemethod that returns one of these classes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I understand the comment. The only relation between the grammars is that they serve the same purpose. Otherwise the strict grammar is based on the RFCs and supports weak ETags. Could you please let me know if my explanation missed the mark?