http.go

  1package lexers
  2
  3import (
  4	"strings"
  5
  6	. "github.com/alecthomas/chroma/v2" // nolint
  7)
  8
  9// HTTP lexer.
 10var HTTP = Register(httpBodyContentTypeLexer(MustNewLexer(
 11	&Config{
 12		Name:         "HTTP",
 13		Aliases:      []string{"http"},
 14		Filenames:    []string{},
 15		MimeTypes:    []string{},
 16		NotMultiline: true,
 17		DotAll:       true,
 18	},
 19	httpRules,
 20)))
 21
 22func httpRules() Rules {
 23	return Rules{
 24		"root": {
 25			{`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([123](?:\.[01])?)(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")},
 26			{`(HTTP)(/)([123](?:\.[01])?)( +)(\d{3})( *)([^\r\n]*)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")},
 27		},
 28		"headers": {
 29			{`([^\s:]+)( *)(:)( *)([^\r\n]+)(\r?\n|\Z)`, EmitterFunc(httpHeaderBlock), nil},
 30			{`([\t ]+)([^\r\n]+)(\r?\n|\Z)`, EmitterFunc(httpContinuousHeaderBlock), nil},
 31			{`\r?\n`, Text, Push("content")},
 32		},
 33		"content": {
 34			{`.+`, EmitterFunc(httpContentBlock), nil},
 35		},
 36	}
 37}
 38
 39func httpContentBlock(groups []string, state *LexerState) Iterator {
 40	tokens := []Token{
 41		{Generic, groups[0]},
 42	}
 43	return Literator(tokens...)
 44}
 45
 46func httpHeaderBlock(groups []string, state *LexerState) Iterator {
 47	tokens := []Token{
 48		{Name, groups[1]},
 49		{Text, groups[2]},
 50		{Operator, groups[3]},
 51		{Text, groups[4]},
 52		{Literal, groups[5]},
 53		{Text, groups[6]},
 54	}
 55	return Literator(tokens...)
 56}
 57
 58func httpContinuousHeaderBlock(groups []string, state *LexerState) Iterator {
 59	tokens := []Token{
 60		{Text, groups[1]},
 61		{Literal, groups[2]},
 62		{Text, groups[3]},
 63	}
 64	return Literator(tokens...)
 65}
 66
 67func httpBodyContentTypeLexer(lexer Lexer) Lexer { return &httpBodyContentTyper{lexer} }
 68
 69type httpBodyContentTyper struct{ Lexer }
 70
 71func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (Iterator, error) { // nolint: gocognit
 72	var contentType string
 73	var isContentType bool
 74	var subIterator Iterator
 75
 76	it, err := d.Lexer.Tokenise(options, text)
 77	if err != nil {
 78		return nil, err
 79	}
 80
 81	return func() Token {
 82		token := it()
 83
 84		if token == EOF {
 85			if subIterator != nil {
 86				return subIterator()
 87			}
 88			return EOF
 89		}
 90
 91		switch {
 92		case token.Type == Name && strings.ToLower(token.Value) == "content-type":
 93			{
 94				isContentType = true
 95			}
 96		case token.Type == Literal && isContentType:
 97			{
 98				isContentType = false
 99				contentType = strings.TrimSpace(token.Value)
100				pos := strings.Index(contentType, ";")
101				if pos > 0 {
102					contentType = strings.TrimSpace(contentType[:pos])
103				}
104			}
105		case token.Type == Generic && contentType != "":
106			{
107				lexer := MatchMimeType(contentType)
108
109				// application/calendar+xml can be treated as application/xml
110				// if there's not a better match.
111				if lexer == nil && strings.Contains(contentType, "+") {
112					slashPos := strings.Index(contentType, "/")
113					plusPos := strings.LastIndex(contentType, "+")
114					contentType = contentType[:slashPos+1] + contentType[plusPos+1:]
115					lexer = MatchMimeType(contentType)
116				}
117
118				if lexer == nil {
119					token.Type = Text
120				} else {
121					subIterator, err = lexer.Tokenise(nil, token.Value)
122					if err != nil {
123						panic(err)
124					}
125					return EOF
126				}
127			}
128		}
129		return token
130	}, nil
131}