1 | package lexers
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "strings"
|
---|
5 |
|
---|
6 | . "github.com/alecthomas/chroma/v2" // nolint
|
---|
7 | )
|
---|
8 |
|
---|
9 | // HTTP lexer.
|
---|
10 | var 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 |
|
---|
22 | func httpRules() Rules {
|
---|
23 | return Rules{
|
---|
24 | "root": {
|
---|
25 | {`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([12]\.[01])(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")},
|
---|
26 | {`(HTTP)(/)([12]\.[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 |
|
---|
39 | func httpContentBlock(groups []string, state *LexerState) Iterator {
|
---|
40 | tokens := []Token{
|
---|
41 | {Generic, groups[0]},
|
---|
42 | }
|
---|
43 | return Literator(tokens...)
|
---|
44 | }
|
---|
45 |
|
---|
46 | func 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 |
|
---|
58 | func 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 |
|
---|
67 | func httpBodyContentTypeLexer(lexer Lexer) Lexer { return &httpBodyContentTyper{lexer} }
|
---|
68 |
|
---|
69 | type httpBodyContentTyper struct{ Lexer }
|
---|
70 |
|
---|
71 | func (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 | }
|
---|