pkg/dataplane/internal/challenge/parse.go (84 lines of code) (raw):
package challenge
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/antlr4-go/antlr/v4"
)
type Listener struct {
challenges []Challenge
errors []error
*BaseChallengeListener
}
// EnterChallenge is called when production challenge is entered.
func (s *Listener) EnterChallenge(ctx *ChallengeContext) {
challenge := Challenge{
Scheme: ctx.Auth_scheme().GetText(),
Parameters: map[string]string{},
}
for _, list := range ctx.AllAuth_params() {
for _, param := range list.AllAuth_param() {
rhs := param.Auth_rhs().GetText()
if param.Auth_rhs().Quoted_string() != nil {
value, err := strconv.Unquote(param.Auth_rhs().Quoted_string().GetText())
if err != nil {
s.errors = append(s.errors, fmt.Errorf("failed to unquote %s: %w", param.Auth_rhs().Quoted_string().GetText(), err))
return
}
rhs = value
}
challenge.Parameters[param.Auth_lhs().GetText()] = rhs
}
for _, value := range ctx.AllToken68() {
challenge.Values = append(challenge.Values, value.GetText())
}
}
s.challenges = append(s.challenges, challenge)
}
type errorSink struct {
*antlr.DefaultErrorListener
errors []error
}
func (e *errorSink) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, exception antlr.RecognitionException) {
e.errors = append(e.errors, fmt.Errorf("syntax error in line %d:%d: %v: %s", line, column, offendingSymbol, msg))
}
var _ antlr.ErrorListener = (*errorSink)(nil)
func Parse(headers http.Header) ([]Challenge, error) {
var challenges []Challenge
var errors []error
parsingErrors := &errorSink{}
for _, value := range headers.Values("WWW-Authenticate") {
p := NewChallengeParser(
antlr.NewCommonTokenStream(
NewChallengeLexer(
antlr.NewInputStream(value),
),
0,
),
)
p.AddErrorListener(parsingErrors)
listener := &Listener{}
antlr.ParseTreeWalkerDefault.Walk(listener, p.Header())
challenges = append(challenges, listener.challenges...)
errors = append(errors, listener.errors...)
}
if parsingErrors.errors != nil {
return nil, collapseErrors(parsingErrors.errors)
}
if errors != nil {
return nil, collapseErrors(errors)
}
return challenges, nil
}
func collapseErrors(errors []error) error {
var reasons []string
for _, err := range errors {
reasons = append(reasons, err.Error())
}
return fmt.Errorf("parsing failed: %s", strings.Join(reasons, ","))
}
type Challenge struct {
Scheme string `json:"scheme"`
Parameters map[string]string `json:"parameters"`
Values []string `json:"values"`
}