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)(/)([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. 
    
  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. }