Skip to content

Commit 3f87da5

Browse files
Scan body redirects and refs (#135)
1 parent 137aaf9 commit 3f87da5

File tree

6 files changed

+182
-2
lines changed

6 files changed

+182
-2
lines changed

internal/syntax/scanner/v2/scanner.go

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,17 @@ func scanHTTPVersion(s *Scanner) stateFn {
611611

612612
s.emit(token.HTTPVersion)
613613

614-
return scanHeaders
614+
s.skip(unicode.IsSpace)
615+
616+
if isIdent(s.peek()) {
617+
return scanHeaders
618+
}
619+
620+
if s.atEOF() || s.restHasPrefix("###") {
621+
return scanStart
622+
}
623+
624+
return scanBody
615625
}
616626

617627
// scanHeaders scans a request header.
@@ -656,9 +666,91 @@ func scanHeaders(s *Scanner) stateFn {
656666

657667
// scanBody scans a HTTP request body.
658668
func scanBody(s *Scanner) stateFn {
659-
s.takeUntil(eof, '#')
669+
// Reading the request body from a file
670+
if s.take("<") {
671+
return scanLeftAngle
672+
}
673+
674+
// Redirecting the response without specifying a body
675+
// e.g. in a GET request
676+
if s.take(">") {
677+
return scanRightAngle
678+
}
679+
680+
s.takeUntil('#', '>', '<', eof)
660681
s.emit(token.Text)
661682

683+
s.skip(unicode.IsSpace)
684+
685+
// Are we doing the response reference pattern e.g. '<> response.json'
686+
if s.take("<") {
687+
return scanLeftAngle
688+
}
689+
690+
// Are we redirecting the response *after* a body has been specified
691+
// e.g. in a POST request, there may be a body *and* a redirect
692+
if s.take(">") {
693+
return scanRightAngle
694+
}
695+
696+
return scanStart
697+
}
698+
699+
// scanLeftAngle scans a '<' in the context of reading a request
700+
// body from the filepath specified next.
701+
//
702+
// It assumes the '<' has already been consumed.
703+
func scanLeftAngle(s *Scanner) stateFn {
704+
if s.take(">") {
705+
// It's a response reference '<>'
706+
s.emit(token.ResponseRef)
707+
} else {
708+
s.emit(token.LeftAngle)
709+
}
710+
711+
s.skip(isLineSpace)
712+
713+
// TODO(@FollowTheProcess): Interps here too
714+
715+
if isFilePath(s.peek()) {
716+
s.takeWhile(isText)
717+
s.emit(token.Text)
718+
}
719+
720+
s.skip(unicode.IsSpace)
721+
722+
// Is the next thing a response ref '<>', which would be the case
723+
// if a request has a body file *and* a response ref
724+
if s.take("<") {
725+
return scanLeftAngle
726+
}
727+
728+
// Are we redirecting the response *after* a body has been specified by a file
729+
if s.take(">") {
730+
return scanRightAngle
731+
}
732+
733+
// Back to start because this marks the end of the request
734+
return scanStart
735+
}
736+
737+
// scanRightAngle scans a '>' in the context of redirecting a response body
738+
// to a filepath specified next.
739+
//
740+
// It assumes the '>' has already been consumed.
741+
func scanRightAngle(s *Scanner) stateFn {
742+
s.emit(token.RightAngle)
743+
744+
s.skip(isLineSpace)
745+
746+
// TODO(@FollowTheProcess): Interp here too
747+
748+
if isFilePath(s.peek()) {
749+
s.takeWhile(isText)
750+
s.emit(token.Text)
751+
}
752+
753+
// Back to start because this marks the end of the request
662754
return scanStart
663755
}
664756

@@ -708,3 +800,8 @@ func isText(r rune) bool {
708800
func isURL(r rune) bool {
709801
return isAlphaNumeric(r) || strings.ContainsRune("$-_.+!*'(),:/?#[]@&;=", r)
710802
}
803+
804+
// isFilePath reports whether r could be a valid first character in a filepath.
805+
func isFilePath(r rune) bool {
806+
return isIdent(r) || r == '.' || r == '/' || r == '\\'
807+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- src.http --
2+
### Body File
3+
POST https://api.somewhere.com/items/1
4+
5+
< ./request.json
6+
7+
> ./response.json
8+
-- tokens.txt --
9+
<Token::Separator start=0, end=3>
10+
<Token::Comment start=4, end=13>
11+
<Token::MethodPost start=14, end=18>
12+
<Token::Text start=19, end=52>
13+
<Token::LeftAngle start=54, end=55>
14+
<Token::Text start=56, end=70>
15+
<Token::RightAngle start=72, end=73>
16+
<Token::Text start=74, end=89>
17+
<Token::EOF start=90, end=90>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- src.http --
2+
### Body File
3+
POST https://api.somewhere.com/items/1 HTTP/1.2
4+
5+
< ./input.json
6+
7+
<> ./output.json
8+
-- tokens.txt --
9+
<Token::Separator start=0, end=3>
10+
<Token::Comment start=4, end=13>
11+
<Token::MethodPost start=14, end=18>
12+
<Token::Text start=19, end=52>
13+
<Token::HTTPVersion start=53, end=61>
14+
<Token::LeftAngle start=63, end=64>
15+
<Token::Text start=65, end=77>
16+
<Token::ResponseRef start=79, end=81>
17+
<Token::Text start=82, end=95>
18+
<Token::EOF start=96, end=96>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- src.http --
2+
###
3+
POST https://api.something.com/items
4+
5+
< ../requests/input.json
6+
-- tokens.txt --
7+
<Token::Separator start=0, end=3>
8+
<Token::MethodPost start=4, end=8>
9+
<Token::Text start=9, end=40>
10+
<Token::LeftAngle start=42, end=43>
11+
<Token::Text start=44, end=66>
12+
<Token::EOF start=67, end=67>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- src.http --
2+
### Body
3+
POST https://api.somewhere.com/items/1
4+
5+
{
6+
"something": "here"
7+
}
8+
9+
> ./response.json
10+
-- tokens.txt --
11+
<Token::Separator start=0, end=3>
12+
<Token::Comment start=4, end=8>
13+
<Token::MethodPost start=9, end=13>
14+
<Token::Text start=14, end=47>
15+
<Token::Text start=49, end=76>
16+
<Token::RightAngle start=76, end=77>
17+
<Token::Text start=78, end=93>
18+
<Token::EOF start=94, end=94>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- src.http --
2+
### Body
3+
POST https://api.somewhere.com/items/1
4+
5+
{
6+
"something": "here"
7+
}
8+
9+
<> response.json
10+
-- tokens.txt --
11+
<Token::Separator start=0, end=3>
12+
<Token::Comment start=4, end=8>
13+
<Token::MethodPost start=9, end=13>
14+
<Token::Text start=14, end=47>
15+
<Token::Text start=49, end=76>
16+
<Token::ResponseRef start=76, end=78>
17+
<Token::Text start=79, end=92>
18+
<Token::EOF start=93, end=93>

0 commit comments

Comments
 (0)