package fasthttp
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
)
const (
rChar = byte ('\r' )
nChar = byte ('\n' )
)
type ResponseHeader struct {
noCopy noCopy
disableNormalizing bool
noHTTP11 bool
connectionClose bool
noDefaultContentType bool
noDefaultDate bool
statusCode int
statusMessage []byte
protocol []byte
contentLength int
contentLengthBytes []byte
secureErrorLogMessage bool
contentType []byte
contentEncoding []byte
server []byte
mulHeader [][]byte
h []argsKV
trailer []argsKV
bufKV argsKV
cookies []argsKV
}
type RequestHeader struct {
noCopy noCopy
disableNormalizing bool
noHTTP11 bool
connectionClose bool
noDefaultContentType bool
disableSpecialHeader bool
cookiesCollected bool
contentLength int
contentLengthBytes []byte
secureErrorLogMessage bool
method []byte
requestURI []byte
proto []byte
host []byte
contentType []byte
userAgent []byte
mulHeader [][]byte
h []argsKV
trailer []argsKV
bufKV argsKV
cookies []argsKV
rawHeaders []byte
}
func (h *ResponseHeader ) SetContentRange (startPos , endPos , contentLength int ) {
b := h .bufKV .value [:0 ]
b = append (b , strBytes ...)
b = append (b , ' ' )
b = AppendUint (b , startPos )
b = append (b , '-' )
b = AppendUint (b , endPos )
b = append (b , '/' )
b = AppendUint (b , contentLength )
h .bufKV .value = b
h .setNonSpecial (strContentRange , h .bufKV .value )
}
func (h *RequestHeader ) SetByteRange (startPos , endPos int ) {
b := h .bufKV .value [:0 ]
b = append (b , strBytes ...)
b = append (b , '=' )
if startPos >= 0 {
b = AppendUint (b , startPos )
} else {
endPos = -startPos
}
b = append (b , '-' )
if endPos >= 0 {
b = AppendUint (b , endPos )
}
h .bufKV .value = b
h .setNonSpecial (strRange , h .bufKV .value )
}
func (h *ResponseHeader ) StatusCode () int {
if h .statusCode == 0 {
return StatusOK
}
return h .statusCode
}
func (h *ResponseHeader ) SetStatusCode (statusCode int ) {
h .statusCode = statusCode
}
func (h *ResponseHeader ) StatusMessage () []byte {
return h .statusMessage
}
func (h *ResponseHeader ) SetStatusMessage (statusMessage []byte ) {
h .statusMessage = append (h .statusMessage [:0 ], statusMessage ...)
}
func (h *ResponseHeader ) Protocol () []byte {
if len (h .protocol ) > 0 {
return h .protocol
}
return strHTTP11
}
func (h *ResponseHeader ) SetProtocol (protocol []byte ) {
h .protocol = append (h .protocol [:0 ], protocol ...)
}
func (h *ResponseHeader ) SetLastModified (t time .Time ) {
h .bufKV .value = AppendHTTPDate (h .bufKV .value [:0 ], t )
h .setNonSpecial (strLastModified , h .bufKV .value )
}
func (h *ResponseHeader ) ConnectionClose () bool {
return h .connectionClose
}
func (h *ResponseHeader ) SetConnectionClose () {
h .connectionClose = true
}
func (h *ResponseHeader ) ResetConnectionClose () {
if h .connectionClose {
h .connectionClose = false
h .h = delAllArgsBytes (h .h , strConnection )
}
}
func (h *RequestHeader ) ConnectionClose () bool {
return h .connectionClose
}
func (h *RequestHeader ) SetConnectionClose () {
h .connectionClose = true
}
func (h *RequestHeader ) ResetConnectionClose () {
if h .connectionClose {
h .connectionClose = false
h .h = delAllArgsBytes (h .h , strConnection )
}
}
func (h *ResponseHeader ) ConnectionUpgrade () bool {
return hasHeaderValue (h .Peek (HeaderConnection ), strUpgrade )
}
func (h *RequestHeader ) ConnectionUpgrade () bool {
return hasHeaderValue (h .Peek (HeaderConnection ), strUpgrade )
}
func (h *ResponseHeader ) PeekCookie (key string ) []byte {
return peekArgStr (h .cookies , key )
}
func (h *ResponseHeader ) ContentLength () int {
return h .contentLength
}
func (h *ResponseHeader ) SetContentLength (contentLength int ) {
if h .mustSkipContentLength () {
return
}
h .contentLength = contentLength
if contentLength >= 0 {
h .contentLengthBytes = AppendUint (h .contentLengthBytes [:0 ], contentLength )
h .h = delAllArgsBytes (h .h , strTransferEncoding )
} else {
h .contentLengthBytes = h .contentLengthBytes [:0 ]
value := strChunked
if contentLength == -2 {
h .SetConnectionClose ()
value = strIdentity
}
h .h = setArgBytes (h .h , strTransferEncoding , value , argsHasValue )
}
}
func (h *ResponseHeader ) mustSkipContentLength () bool {
statusCode := h .StatusCode ()
if statusCode < 100 || statusCode == StatusOK {
return false
}
return statusCode == StatusNotModified || statusCode == StatusNoContent || statusCode < 200
}
func (h *RequestHeader ) ContentLength () int {
return h .realContentLength ()
}
func (h *RequestHeader ) realContentLength () int {
return h .contentLength
}
func (h *RequestHeader ) SetContentLength (contentLength int ) {
h .contentLength = contentLength
if contentLength >= 0 {
h .contentLengthBytes = AppendUint (h .contentLengthBytes [:0 ], contentLength )
h .h = delAllArgsBytes (h .h , strTransferEncoding )
} else {
h .contentLengthBytes = h .contentLengthBytes [:0 ]
h .h = setArgBytes (h .h , strTransferEncoding , strChunked , argsHasValue )
}
}
func (h *ResponseHeader ) isCompressibleContentType () bool {
contentType := h .ContentType ()
return bytes .HasPrefix (contentType , strTextSlash ) ||
bytes .HasPrefix (contentType , strApplicationSlash ) ||
bytes .HasPrefix (contentType , strImageSVG ) ||
bytes .HasPrefix (contentType , strImageIcon ) ||
bytes .HasPrefix (contentType , strFontSlash ) ||
bytes .HasPrefix (contentType , strMultipartSlash )
}
func (h *ResponseHeader ) ContentType () []byte {
contentType := h .contentType
if !h .noDefaultContentType && len (h .contentType ) == 0 {
contentType = defaultContentType
}
return contentType
}
func (h *ResponseHeader ) SetContentType (contentType string ) {
h .contentType = append (h .contentType [:0 ], contentType ...)
}
func (h *ResponseHeader ) SetContentTypeBytes (contentType []byte ) {
h .contentType = append (h .contentType [:0 ], contentType ...)
}
func (h *ResponseHeader ) ContentEncoding () []byte {
return h .contentEncoding
}
func (h *ResponseHeader ) SetContentEncoding (contentEncoding string ) {
h .contentEncoding = append (h .contentEncoding [:0 ], contentEncoding ...)
}
func (h *ResponseHeader ) SetContentEncodingBytes (contentEncoding []byte ) {
h .contentEncoding = append (h .contentEncoding [:0 ], contentEncoding ...)
}
func (h *ResponseHeader ) addVaryBytes (value []byte ) {
v := h .peek (strVary )
if len (v ) == 0 {
h .SetBytesV (HeaderVary , value )
} else if !bytes .Contains (v , value ) {
h .SetBytesV (HeaderVary , append (append (v , ',' ), value ...))
}
}
func (h *ResponseHeader ) Server () []byte {
return h .server
}
func (h *ResponseHeader ) SetServer (server string ) {
h .server = append (h .server [:0 ], server ...)
}
func (h *ResponseHeader ) SetServerBytes (server []byte ) {
h .server = append (h .server [:0 ], server ...)
}
func (h *RequestHeader ) ContentType () []byte {
if h .disableSpecialHeader {
return peekArgBytes (h .h , []byte (HeaderContentType ))
}
return h .contentType
}
func (h *RequestHeader ) SetContentType (contentType string ) {
h .contentType = append (h .contentType [:0 ], contentType ...)
}
func (h *RequestHeader ) SetContentTypeBytes (contentType []byte ) {
h .contentType = append (h .contentType [:0 ], contentType ...)
}
func (h *RequestHeader ) ContentEncoding () []byte {
return peekArgBytes (h .h , strContentEncoding )
}
func (h *RequestHeader ) SetContentEncoding (contentEncoding string ) {
h .SetBytesK (strContentEncoding , contentEncoding )
}
func (h *RequestHeader ) SetContentEncodingBytes (contentEncoding []byte ) {
h .setNonSpecial (strContentEncoding , contentEncoding )
}
func (h *RequestHeader ) SetMultipartFormBoundary (boundary string ) {
b := h .bufKV .value [:0 ]
b = append (b , strMultipartFormData ...)
b = append (b , ';' , ' ' )
b = append (b , strBoundary ...)
b = append (b , '=' )
b = append (b , boundary ...)
h .bufKV .value = b
h .SetContentTypeBytes (h .bufKV .value )
}
func (h *RequestHeader ) SetMultipartFormBoundaryBytes (boundary []byte ) {
b := h .bufKV .value [:0 ]
b = append (b , strMultipartFormData ...)
b = append (b , ';' , ' ' )
b = append (b , strBoundary ...)
b = append (b , '=' )
b = append (b , boundary ...)
h .bufKV .value = b
h .SetContentTypeBytes (h .bufKV .value )
}
func (h *ResponseHeader ) SetTrailer (trailer string ) error {
return h .SetTrailerBytes (s2b (trailer ))
}
func (h *ResponseHeader ) SetTrailerBytes (trailer []byte ) error {
h .trailer = h .trailer [:0 ]
return h .AddTrailerBytes (trailer )
}
func (h *ResponseHeader ) AddTrailer (trailer string ) error {
return h .AddTrailerBytes (s2b (trailer ))
}
var ErrBadTrailer = errors .New ("contain forbidden trailer" )
func (h *ResponseHeader ) AddTrailerBytes (trailer []byte ) error {
var err error
for i := -1 ; i +1 < len (trailer ); {
trailer = trailer [i +1 :]
i = bytes .IndexByte (trailer , ',' )
if i < 0 {
i = len (trailer )
}
key := trailer [:i ]
for len (key ) > 0 && key [0 ] == ' ' {
key = key [1 :]
}
for len (key ) > 0 && key [len (key )-1 ] == ' ' {
key = key [:len (key )-1 ]
}
if isBadTrailer (key ) {
err = ErrBadTrailer
continue
}
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
h .trailer = appendArgBytes (h .trailer , h .bufKV .key , nil , argsNoValue )
}
return err
}
func (h *RequestHeader ) MultipartFormBoundary () []byte {
b := h .ContentType ()
if !bytes .HasPrefix (b , strMultipartFormData ) {
return nil
}
b = b [len (strMultipartFormData ):]
if len (b ) == 0 || b [0 ] != ';' {
return nil
}
var n int
for len (b ) > 0 {
n ++
for len (b ) > n && b [n ] == ' ' {
n ++
}
b = b [n :]
if !bytes .HasPrefix (b , strBoundary ) {
if n = bytes .IndexByte (b , ';' ); n < 0 {
return nil
}
continue
}
b = b [len (strBoundary ):]
if len (b ) == 0 || b [0 ] != '=' {
return nil
}
b = b [1 :]
if n = bytes .IndexByte (b , ';' ); n >= 0 {
b = b [:n ]
}
if len (b ) > 1 && b [0 ] == '"' && b [len (b )-1 ] == '"' {
b = b [1 : len (b )-1 ]
}
return b
}
return nil
}
func (h *RequestHeader ) Host () []byte {
if h .disableSpecialHeader {
return peekArgBytes (h .h , []byte (HeaderHost ))
}
return h .host
}
func (h *RequestHeader ) SetHost (host string ) {
h .host = append (h .host [:0 ], host ...)
}
func (h *RequestHeader ) SetHostBytes (host []byte ) {
h .host = append (h .host [:0 ], host ...)
}
func (h *RequestHeader ) UserAgent () []byte {
if h .disableSpecialHeader {
return peekArgBytes (h .h , []byte (HeaderUserAgent ))
}
return h .userAgent
}
func (h *RequestHeader ) SetUserAgent (userAgent string ) {
h .userAgent = append (h .userAgent [:0 ], userAgent ...)
}
func (h *RequestHeader ) SetUserAgentBytes (userAgent []byte ) {
h .userAgent = append (h .userAgent [:0 ], userAgent ...)
}
func (h *RequestHeader ) Referer () []byte {
return peekArgBytes (h .h , strReferer )
}
func (h *RequestHeader ) SetReferer (referer string ) {
h .SetBytesK (strReferer , referer )
}
func (h *RequestHeader ) SetRefererBytes (referer []byte ) {
h .setNonSpecial (strReferer , referer )
}
func (h *RequestHeader ) Method () []byte {
if len (h .method ) == 0 {
return []byte (MethodGet )
}
return h .method
}
func (h *RequestHeader ) SetMethod (method string ) {
h .method = append (h .method [:0 ], method ...)
}
func (h *RequestHeader ) SetMethodBytes (method []byte ) {
h .method = append (h .method [:0 ], method ...)
}
func (h *RequestHeader ) Protocol () []byte {
if len (h .proto ) == 0 {
return strHTTP11
}
return h .proto
}
func (h *RequestHeader ) SetProtocol (method string ) {
h .proto = append (h .proto [:0 ], method ...)
h .noHTTP11 = !bytes .Equal (h .proto , strHTTP11 )
}
func (h *RequestHeader ) SetProtocolBytes (method []byte ) {
h .proto = append (h .proto [:0 ], method ...)
h .noHTTP11 = !bytes .Equal (h .proto , strHTTP11 )
}
func (h *RequestHeader ) RequestURI () []byte {
requestURI := h .requestURI
if len (requestURI ) == 0 {
requestURI = strSlash
}
return requestURI
}
func (h *RequestHeader ) SetRequestURI (requestURI string ) {
h .requestURI = append (h .requestURI [:0 ], requestURI ...)
}
func (h *RequestHeader ) SetRequestURIBytes (requestURI []byte ) {
h .requestURI = append (h .requestURI [:0 ], requestURI ...)
}
func (h *RequestHeader ) SetTrailer (trailer string ) error {
return h .SetTrailerBytes (s2b (trailer ))
}
func (h *RequestHeader ) SetTrailerBytes (trailer []byte ) error {
h .trailer = h .trailer [:0 ]
return h .AddTrailerBytes (trailer )
}
func (h *RequestHeader ) AddTrailer (trailer string ) error {
return h .AddTrailerBytes (s2b (trailer ))
}
func (h *RequestHeader ) AddTrailerBytes (trailer []byte ) error {
var err error
for i := -1 ; i +1 < len (trailer ); {
trailer = trailer [i +1 :]
i = bytes .IndexByte (trailer , ',' )
if i < 0 {
i = len (trailer )
}
key := trailer [:i ]
for len (key ) > 0 && key [0 ] == ' ' {
key = key [1 :]
}
for len (key ) > 0 && key [len (key )-1 ] == ' ' {
key = key [:len (key )-1 ]
}
if isBadTrailer (key ) {
err = ErrBadTrailer
continue
}
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
h .trailer = appendArgBytes (h .trailer , h .bufKV .key , nil , argsNoValue )
}
return err
}
func (h *RequestHeader ) IsGet () bool {
return string (h .Method ()) == MethodGet
}
func (h *RequestHeader ) IsPost () bool {
return string (h .Method ()) == MethodPost
}
func (h *RequestHeader ) IsPut () bool {
return string (h .Method ()) == MethodPut
}
func (h *RequestHeader ) IsHead () bool {
return string (h .Method ()) == MethodHead
}
func (h *RequestHeader ) IsDelete () bool {
return string (h .Method ()) == MethodDelete
}
func (h *RequestHeader ) IsConnect () bool {
return string (h .Method ()) == MethodConnect
}
func (h *RequestHeader ) IsOptions () bool {
return string (h .Method ()) == MethodOptions
}
func (h *RequestHeader ) IsTrace () bool {
return string (h .Method ()) == MethodTrace
}
func (h *RequestHeader ) IsPatch () bool {
return string (h .Method ()) == MethodPatch
}
func (h *RequestHeader ) IsHTTP11 () bool {
return !h .noHTTP11
}
func (h *ResponseHeader ) IsHTTP11 () bool {
return !h .noHTTP11
}
func (h *RequestHeader ) HasAcceptEncoding (acceptEncoding string ) bool {
h .bufKV .value = append (h .bufKV .value [:0 ], acceptEncoding ...)
return h .HasAcceptEncodingBytes (h .bufKV .value )
}
func (h *RequestHeader ) HasAcceptEncodingBytes (acceptEncoding []byte ) bool {
ae := h .peek (strAcceptEncoding )
n := bytes .Index (ae , acceptEncoding )
if n < 0 {
return false
}
b := ae [n +len (acceptEncoding ):]
if len (b ) > 0 && b [0 ] != ',' {
return false
}
if n == 0 {
return true
}
return ae [n -1 ] == ' '
}
func (h *ResponseHeader ) Len () int {
n := 0
h .VisitAll (func (_ , _ []byte ) { n ++ })
return n
}
func (h *RequestHeader ) Len () int {
n := 0
h .VisitAll (func (_ , _ []byte ) { n ++ })
return n
}
func (h *RequestHeader ) DisableSpecialHeader () {
h .disableSpecialHeader = true
}
func (h *RequestHeader ) EnableSpecialHeader () {
h .disableSpecialHeader = false
}
func (h *RequestHeader ) DisableNormalizing () {
h .disableNormalizing = true
}
func (h *RequestHeader ) EnableNormalizing () {
h .disableNormalizing = false
}
func (h *ResponseHeader ) DisableNormalizing () {
h .disableNormalizing = true
}
func (h *ResponseHeader ) EnableNormalizing () {
h .disableNormalizing = false
}
func (h *ResponseHeader ) SetNoDefaultContentType (noDefaultContentType bool ) {
h .noDefaultContentType = noDefaultContentType
}
func (h *ResponseHeader ) Reset () {
h .disableNormalizing = false
h .SetNoDefaultContentType (false )
h .noDefaultDate = false
h .resetSkipNormalize ()
}
func (h *ResponseHeader ) resetSkipNormalize () {
h .noHTTP11 = false
h .connectionClose = false
h .statusCode = 0
h .statusMessage = h .statusMessage [:0 ]
h .protocol = h .protocol [:0 ]
h .contentLength = 0
h .contentLengthBytes = h .contentLengthBytes [:0 ]
h .contentType = h .contentType [:0 ]
h .contentEncoding = h .contentEncoding [:0 ]
h .server = h .server [:0 ]
h .h = h .h [:0 ]
h .cookies = h .cookies [:0 ]
h .trailer = h .trailer [:0 ]
h .mulHeader = h .mulHeader [:0 ]
}
func (h *RequestHeader ) SetNoDefaultContentType (noDefaultContentType bool ) {
h .noDefaultContentType = noDefaultContentType
}
func (h *RequestHeader ) Reset () {
h .disableNormalizing = false
h .SetNoDefaultContentType (false )
h .resetSkipNormalize ()
}
func (h *RequestHeader ) resetSkipNormalize () {
h .noHTTP11 = false
h .connectionClose = false
h .contentLength = 0
h .contentLengthBytes = h .contentLengthBytes [:0 ]
h .method = h .method [:0 ]
h .proto = h .proto [:0 ]
h .requestURI = h .requestURI [:0 ]
h .host = h .host [:0 ]
h .contentType = h .contentType [:0 ]
h .userAgent = h .userAgent [:0 ]
h .trailer = h .trailer [:0 ]
h .mulHeader = h .mulHeader [:0 ]
h .h = h .h [:0 ]
h .cookies = h .cookies [:0 ]
h .cookiesCollected = false
h .rawHeaders = h .rawHeaders [:0 ]
}
func (h *ResponseHeader ) CopyTo (dst *ResponseHeader ) {
dst .Reset ()
dst .disableNormalizing = h .disableNormalizing
dst .noHTTP11 = h .noHTTP11
dst .connectionClose = h .connectionClose
dst .noDefaultContentType = h .noDefaultContentType
dst .noDefaultDate = h .noDefaultDate
dst .statusCode = h .statusCode
dst .statusMessage = append (dst .statusMessage , h .statusMessage ...)
dst .protocol = append (dst .protocol , h .protocol ...)
dst .contentLength = h .contentLength
dst .contentLengthBytes = append (dst .contentLengthBytes , h .contentLengthBytes ...)
dst .contentType = append (dst .contentType , h .contentType ...)
dst .contentEncoding = append (dst .contentEncoding , h .contentEncoding ...)
dst .server = append (dst .server , h .server ...)
dst .h = copyArgs (dst .h , h .h )
dst .cookies = copyArgs (dst .cookies , h .cookies )
dst .trailer = copyArgs (dst .trailer , h .trailer )
}
func (h *RequestHeader ) CopyTo (dst *RequestHeader ) {
dst .Reset ()
dst .disableNormalizing = h .disableNormalizing
dst .noHTTP11 = h .noHTTP11
dst .connectionClose = h .connectionClose
dst .noDefaultContentType = h .noDefaultContentType
dst .contentLength = h .contentLength
dst .contentLengthBytes = append (dst .contentLengthBytes , h .contentLengthBytes ...)
dst .method = append (dst .method , h .method ...)
dst .proto = append (dst .proto , h .proto ...)
dst .requestURI = append (dst .requestURI , h .requestURI ...)
dst .host = append (dst .host , h .host ...)
dst .contentType = append (dst .contentType , h .contentType ...)
dst .userAgent = append (dst .userAgent , h .userAgent ...)
dst .trailer = append (dst .trailer , h .trailer ...)
dst .h = copyArgs (dst .h , h .h )
dst .cookies = copyArgs (dst .cookies , h .cookies )
dst .cookiesCollected = h .cookiesCollected
dst .rawHeaders = append (dst .rawHeaders , h .rawHeaders ...)
}
func (h *ResponseHeader ) VisitAll (f func (key , value []byte )) {
if len (h .contentLengthBytes ) > 0 {
f (strContentLength , h .contentLengthBytes )
}
contentType := h .ContentType ()
if len (contentType ) > 0 {
f (strContentType , contentType )
}
contentEncoding := h .ContentEncoding ()
if len (contentEncoding ) > 0 {
f (strContentEncoding , contentEncoding )
}
server := h .Server ()
if len (server ) > 0 {
f (strServer , server )
}
if len (h .cookies ) > 0 {
visitArgs (h .cookies , func (_ , v []byte ) {
f (strSetCookie , v )
})
}
if len (h .trailer ) > 0 {
f (strTrailer , appendArgsKeyBytes (nil , h .trailer , strCommaSpace ))
}
visitArgs (h .h , f )
if h .ConnectionClose () {
f (strConnection , strClose )
}
}
func (h *ResponseHeader ) VisitAllTrailer (f func (value []byte )) {
visitArgsKey (h .trailer , f )
}
func (h *RequestHeader ) VisitAllTrailer (f func (value []byte )) {
visitArgsKey (h .trailer , f )
}
func (h *ResponseHeader ) VisitAllCookie (f func (key , value []byte )) {
visitArgs (h .cookies , f )
}
func (h *RequestHeader ) VisitAllCookie (f func (key , value []byte )) {
h .collectCookies ()
visitArgs (h .cookies , f )
}
func (h *RequestHeader ) VisitAll (f func (key , value []byte )) {
host := h .Host ()
if len (host ) > 0 {
f (strHost , host )
}
if len (h .contentLengthBytes ) > 0 {
f (strContentLength , h .contentLengthBytes )
}
contentType := h .ContentType ()
if len (contentType ) > 0 {
f (strContentType , contentType )
}
userAgent := h .UserAgent ()
if len (userAgent ) > 0 {
f (strUserAgent , userAgent )
}
if len (h .trailer ) > 0 {
f (strTrailer , appendArgsKeyBytes (nil , h .trailer , strCommaSpace ))
}
h .collectCookies ()
if len (h .cookies ) > 0 {
h .bufKV .value = appendRequestCookieBytes (h .bufKV .value [:0 ], h .cookies )
f (strCookie , h .bufKV .value )
}
visitArgs (h .h , f )
if h .ConnectionClose () {
f (strConnection , strClose )
}
}
func (h *RequestHeader ) VisitAllInOrder (f func (key , value []byte )) {
var s headerScanner
s .b = h .rawHeaders
s .disableNormalizing = h .disableNormalizing
for s .next () {
if len (s .key ) > 0 {
f (s .key , s .value )
}
}
}
func (h *ResponseHeader ) Del (key string ) {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
h .del (k )
}
func (h *ResponseHeader ) DelBytes (key []byte ) {
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
h .del (h .bufKV .key )
}
func (h *ResponseHeader ) del (key []byte ) {
switch string (key ) {
case HeaderContentType :
h .contentType = h .contentType [:0 ]
case HeaderContentEncoding :
h .contentEncoding = h .contentEncoding [:0 ]
case HeaderServer :
h .server = h .server [:0 ]
case HeaderSetCookie :
h .cookies = h .cookies [:0 ]
case HeaderContentLength :
h .contentLength = 0
h .contentLengthBytes = h .contentLengthBytes [:0 ]
case HeaderConnection :
h .connectionClose = false
case HeaderTrailer :
h .trailer = h .trailer [:0 ]
}
h .h = delAllArgsBytes (h .h , key )
}
func (h *RequestHeader ) Del (key string ) {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
h .del (k )
}
func (h *RequestHeader ) DelBytes (key []byte ) {
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
h .del (h .bufKV .key )
}
func (h *RequestHeader ) del (key []byte ) {
switch string (key ) {
case HeaderHost :
h .host = h .host [:0 ]
case HeaderContentType :
h .contentType = h .contentType [:0 ]
case HeaderUserAgent :
h .userAgent = h .userAgent [:0 ]
case HeaderCookie :
h .cookies = h .cookies [:0 ]
case HeaderContentLength :
h .contentLength = 0
h .contentLengthBytes = h .contentLengthBytes [:0 ]
case HeaderConnection :
h .connectionClose = false
case HeaderTrailer :
h .trailer = h .trailer [:0 ]
}
h .h = delAllArgsBytes (h .h , key )
}
func (h *ResponseHeader ) setSpecialHeader (key , value []byte ) bool {
if len (key ) == 0 {
return false
}
switch key [0 ] | 0x20 {
case 'c' :
switch {
case caseInsensitiveCompare (strContentType , key ):
h .SetContentTypeBytes (value )
return true
case caseInsensitiveCompare (strContentLength , key ):
if contentLength , err := parseContentLength (value ); err == nil {
h .contentLength = contentLength
h .contentLengthBytes = append (h .contentLengthBytes [:0 ], value ...)
}
return true
case caseInsensitiveCompare (strContentEncoding , key ):
h .SetContentEncodingBytes (value )
return true
case caseInsensitiveCompare (strConnection , key ):
if bytes .Equal (strClose , value ) {
h .SetConnectionClose ()
} else {
h .ResetConnectionClose ()
h .setNonSpecial (key , value )
}
return true
}
case 's' :
if caseInsensitiveCompare (strServer , key ) {
h .SetServerBytes (value )
return true
} else if caseInsensitiveCompare (strSetCookie , key ) {
var kv *argsKV
h .cookies , kv = allocArg (h .cookies )
kv .key = getCookieKey (kv .key , value )
kv .value = append (kv .value [:0 ], value ...)
return true
}
case 't' :
if caseInsensitiveCompare (strTransferEncoding , key ) {
return true
} else if caseInsensitiveCompare (strTrailer , key ) {
_ = h .SetTrailerBytes (value )
return true
}
case 'd' :
if caseInsensitiveCompare (strDate , key ) {
return true
}
}
return false
}
func (h *ResponseHeader ) setNonSpecial (key []byte , value []byte ) {
h .h = setArgBytes (h .h , key , value , argsHasValue )
}
func (h *RequestHeader ) setSpecialHeader (key , value []byte ) bool {
if len (key ) == 0 || h .disableSpecialHeader {
return false
}
switch key [0 ] | 0x20 {
case 'c' :
switch {
case caseInsensitiveCompare (strContentType , key ):
h .SetContentTypeBytes (value )
return true
case caseInsensitiveCompare (strContentLength , key ):
if contentLength , err := parseContentLength (value ); err == nil {
h .contentLength = contentLength
h .contentLengthBytes = append (h .contentLengthBytes [:0 ], value ...)
}
return true
case caseInsensitiveCompare (strConnection , key ):
if bytes .Equal (strClose , value ) {
h .SetConnectionClose ()
} else {
h .ResetConnectionClose ()
h .setNonSpecial (key , value )
}
return true
case caseInsensitiveCompare (strCookie , key ):
h .collectCookies ()
h .cookies = parseRequestCookies (h .cookies , value )
return true
}
case 't' :
if caseInsensitiveCompare (strTransferEncoding , key ) {
return true
} else if caseInsensitiveCompare (strTrailer , key ) {
_ = h .SetTrailerBytes (value )
return true
}
case 'h' :
if caseInsensitiveCompare (strHost , key ) {
h .SetHostBytes (value )
return true
}
case 'u' :
if caseInsensitiveCompare (strUserAgent , key ) {
h .SetUserAgentBytes (value )
return true
}
}
return false
}
func (h *RequestHeader ) setNonSpecial (key []byte , value []byte ) {
h .h = setArgBytes (h .h , key , value , argsHasValue )
}
func (h *ResponseHeader ) Add (key , value string ) {
h .AddBytesKV (s2b (key ), s2b (value ))
}
func (h *ResponseHeader ) AddBytesK (key []byte , value string ) {
h .AddBytesKV (key , s2b (value ))
}
func (h *ResponseHeader ) AddBytesV (key string , value []byte ) {
h .AddBytesKV (s2b (key ), value )
}
func (h *ResponseHeader ) AddBytesKV (key , value []byte ) {
if h .setSpecialHeader (key , value ) {
return
}
k := getHeaderKeyBytes (&h .bufKV , b2s (key ), h .disableNormalizing )
h .h = appendArgBytes (h .h , k , value , argsHasValue )
}
func (h *ResponseHeader ) Set (key , value string ) {
initHeaderKV (&h .bufKV , key , value , h .disableNormalizing )
h .SetCanonical (h .bufKV .key , h .bufKV .value )
}
func (h *ResponseHeader ) SetBytesK (key []byte , value string ) {
h .bufKV .value = append (h .bufKV .value [:0 ], value ...)
h .SetBytesKV (key , h .bufKV .value )
}
func (h *ResponseHeader ) SetBytesV (key string , value []byte ) {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
h .SetCanonical (k , value )
}
func (h *ResponseHeader ) SetBytesKV (key , value []byte ) {
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
h .SetCanonical (h .bufKV .key , value )
}
func (h *ResponseHeader ) SetCanonical (key , value []byte ) {
if h .setSpecialHeader (key , value ) {
return
}
h .setNonSpecial (key , value )
}
func (h *ResponseHeader ) SetCookie (cookie *Cookie ) {
h .cookies = setArgBytes (h .cookies , cookie .Key (), cookie .Cookie (), argsHasValue )
}
func (h *RequestHeader ) SetCookie (key , value string ) {
h .collectCookies ()
h .cookies = setArg (h .cookies , key , value , argsHasValue )
}
func (h *RequestHeader ) SetCookieBytesK (key []byte , value string ) {
h .SetCookie (b2s (key ), value )
}
func (h *RequestHeader ) SetCookieBytesKV (key , value []byte ) {
h .SetCookie (b2s (key ), b2s (value ))
}
func (h *ResponseHeader ) DelClientCookie (key string ) {
h .DelCookie (key )
c := AcquireCookie ()
c .SetKey (key )
c .SetExpire (CookieExpireDelete )
h .SetCookie (c )
ReleaseCookie (c )
}
func (h *ResponseHeader ) DelClientCookieBytes (key []byte ) {
h .DelClientCookie (b2s (key ))
}
func (h *ResponseHeader ) DelCookie (key string ) {
h .cookies = delAllArgs (h .cookies , key )
}
func (h *ResponseHeader ) DelCookieBytes (key []byte ) {
h .DelCookie (b2s (key ))
}
func (h *RequestHeader ) DelCookie (key string ) {
h .collectCookies ()
h .cookies = delAllArgs (h .cookies , key )
}
func (h *RequestHeader ) DelCookieBytes (key []byte ) {
h .DelCookie (b2s (key ))
}
func (h *ResponseHeader ) DelAllCookies () {
h .cookies = h .cookies [:0 ]
}
func (h *RequestHeader ) DelAllCookies () {
h .collectCookies ()
h .cookies = h .cookies [:0 ]
}
func (h *RequestHeader ) Add (key , value string ) {
h .AddBytesKV (s2b (key ), s2b (value ))
}
func (h *RequestHeader ) AddBytesK (key []byte , value string ) {
h .AddBytesKV (key , s2b (value ))
}
func (h *RequestHeader ) AddBytesV (key string , value []byte ) {
h .AddBytesKV (s2b (key ), value )
}
func (h *RequestHeader ) AddBytesKV (key , value []byte ) {
if h .setSpecialHeader (key , value ) {
return
}
k := getHeaderKeyBytes (&h .bufKV , b2s (key ), h .disableNormalizing )
h .h = appendArgBytes (h .h , k , value , argsHasValue )
}
func (h *RequestHeader ) Set (key , value string ) {
initHeaderKV (&h .bufKV , key , value , h .disableNormalizing )
h .SetCanonical (h .bufKV .key , h .bufKV .value )
}
func (h *RequestHeader ) SetBytesK (key []byte , value string ) {
h .bufKV .value = append (h .bufKV .value [:0 ], value ...)
h .SetBytesKV (key , h .bufKV .value )
}
func (h *RequestHeader ) SetBytesV (key string , value []byte ) {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
h .SetCanonical (k , value )
}
func (h *RequestHeader ) SetBytesKV (key , value []byte ) {
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
h .SetCanonical (h .bufKV .key , value )
}
func (h *RequestHeader ) SetCanonical (key , value []byte ) {
if h .setSpecialHeader (key , value ) {
return
}
h .setNonSpecial (key , value )
}
func (h *ResponseHeader ) Peek (key string ) []byte {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
return h .peek (k )
}
func (h *ResponseHeader ) PeekBytes (key []byte ) []byte {
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
return h .peek (h .bufKV .key )
}
func (h *RequestHeader ) Peek (key string ) []byte {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
return h .peek (k )
}
func (h *RequestHeader ) PeekBytes (key []byte ) []byte {
h .bufKV .key = append (h .bufKV .key [:0 ], key ...)
normalizeHeaderKey (h .bufKV .key , h .disableNormalizing )
return h .peek (h .bufKV .key )
}
func (h *ResponseHeader ) peek (key []byte ) []byte {
switch string (key ) {
case HeaderContentType :
return h .ContentType ()
case HeaderContentEncoding :
return h .ContentEncoding ()
case HeaderServer :
return h .Server ()
case HeaderConnection :
if h .ConnectionClose () {
return strClose
}
return peekArgBytes (h .h , key )
case HeaderContentLength :
return h .contentLengthBytes
case HeaderSetCookie :
return appendResponseCookieBytes (nil , h .cookies )
case HeaderTrailer :
return appendArgsKeyBytes (nil , h .trailer , strCommaSpace )
default :
return peekArgBytes (h .h , key )
}
}
func (h *RequestHeader ) peek (key []byte ) []byte {
switch string (key ) {
case HeaderHost :
return h .Host ()
case HeaderContentType :
return h .ContentType ()
case HeaderUserAgent :
return h .UserAgent ()
case HeaderConnection :
if h .ConnectionClose () {
return strClose
}
return peekArgBytes (h .h , key )
case HeaderContentLength :
return h .contentLengthBytes
case HeaderCookie :
if h .cookiesCollected {
return appendRequestCookieBytes (nil , h .cookies )
}
return peekArgBytes (h .h , key )
case HeaderTrailer :
return appendArgsKeyBytes (nil , h .trailer , strCommaSpace )
default :
return peekArgBytes (h .h , key )
}
}
func (h *RequestHeader ) PeekAll (key string ) [][]byte {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
return h .peekAll (k )
}
func (h *RequestHeader ) peekAll (key []byte ) [][]byte {
h .mulHeader = h .mulHeader [:0 ]
switch string (key ) {
case HeaderHost :
if host := h .Host (); len (host ) > 0 {
h .mulHeader = append (h .mulHeader , host )
}
case HeaderContentType :
if contentType := h .ContentType (); len (contentType ) > 0 {
h .mulHeader = append (h .mulHeader , contentType )
}
case HeaderUserAgent :
if ua := h .UserAgent (); len (ua ) > 0 {
h .mulHeader = append (h .mulHeader , ua )
}
case HeaderConnection :
if h .ConnectionClose () {
h .mulHeader = append (h .mulHeader , strClose )
} else {
h .mulHeader = peekAllArgBytesToDst (h .mulHeader , h .h , key )
}
case HeaderContentLength :
h .mulHeader = append (h .mulHeader , h .contentLengthBytes )
case HeaderCookie :
if h .cookiesCollected {
h .mulHeader = append (h .mulHeader , appendRequestCookieBytes (nil , h .cookies ))
} else {
h .mulHeader = peekAllArgBytesToDst (h .mulHeader , h .h , key )
}
case HeaderTrailer :
h .mulHeader = append (h .mulHeader , appendArgsKeyBytes (nil , h .trailer , strCommaSpace ))
default :
h .mulHeader = peekAllArgBytesToDst (h .mulHeader , h .h , key )
}
return h .mulHeader
}
func (h *ResponseHeader ) PeekAll (key string ) [][]byte {
k := getHeaderKeyBytes (&h .bufKV , key , h .disableNormalizing )
return h .peekAll (k )
}
func (h *ResponseHeader ) peekAll (key []byte ) [][]byte {
h .mulHeader = h .mulHeader [:0 ]
switch string (key ) {
case HeaderContentType :
if contentType := h .ContentType (); len (contentType ) > 0 {
h .mulHeader = append (h .mulHeader , contentType )
}
case HeaderContentEncoding :
if contentEncoding := h .ContentEncoding (); len (contentEncoding ) > 0 {
h .mulHeader = append (h .mulHeader , contentEncoding )
}
case HeaderServer :
if server := h .Server (); len (server ) > 0 {
h .mulHeader = append (h .mulHeader , server )
}
case HeaderConnection :
if h .ConnectionClose () {
h .mulHeader = append (h .mulHeader , strClose )
} else {
h .mulHeader = peekAllArgBytesToDst (h .mulHeader , h .h , key )
}
case HeaderContentLength :
h .mulHeader = append (h .mulHeader , h .contentLengthBytes )
case HeaderSetCookie :
h .mulHeader = append (h .mulHeader , appendResponseCookieBytes (nil , h .cookies ))
case HeaderTrailer :
h .mulHeader = append (h .mulHeader , appendArgsKeyBytes (nil , h .trailer , strCommaSpace ))
default :
h .mulHeader = peekAllArgBytesToDst (h .mulHeader , h .h , key )
}
return h .mulHeader
}
func (h *RequestHeader ) PeekKeys () [][]byte {
h .mulHeader = h .mulHeader [:0 ]
h .mulHeader = peekArgsKeys (h .mulHeader , h .h )
return h .mulHeader
}
func (h *RequestHeader ) PeekTrailerKeys () [][]byte {
h .mulHeader = h .mulHeader [:0 ]
h .mulHeader = peekArgsKeys (h .mulHeader , h .trailer )
return h .mulHeader
}
func (h *ResponseHeader ) PeekKeys () [][]byte {
h .mulHeader = h .mulHeader [:0 ]
h .mulHeader = peekArgsKeys (h .mulHeader , h .h )
return h .mulHeader
}
func (h *ResponseHeader ) PeekTrailerKeys () [][]byte {
h .mulHeader = h .mulHeader [:0 ]
h .mulHeader = peekArgsKeys (h .mulHeader , h .trailer )
return h .mulHeader
}
func (h *RequestHeader ) Cookie (key string ) []byte {
h .collectCookies ()
return peekArgStr (h .cookies , key )
}
func (h *RequestHeader ) CookieBytes (key []byte ) []byte {
h .collectCookies ()
return peekArgBytes (h .cookies , key )
}
func (h *ResponseHeader ) Cookie (cookie *Cookie ) bool {
v := peekArgBytes (h .cookies , cookie .Key ())
if v == nil {
return false
}
cookie .ParseBytes (v )
return true
}
func (h *ResponseHeader ) Read (r *bufio .Reader ) error {
n := 1
for {
err := h .tryRead (r , n )
if err == nil {
return nil
}
if err != errNeedMore {
h .resetSkipNormalize ()
return err
}
n = r .Buffered () + 1
}
}
func (h *ResponseHeader ) tryRead (r *bufio .Reader , n int ) error {
h .resetSkipNormalize ()
b , err := r .Peek (n )
if len (b ) == 0 {
if x , ok := err .(interface { Timeout () bool }); ok && x .Timeout () {
return ErrTimeout
}
if n == 1 || err == io .EOF {
return io .EOF
}
if err == bufio .ErrBufferFull {
if h .secureErrorLogMessage {
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading response headers" ),
}
}
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading response headers: %w" , errSmallBuffer ),
}
}
return fmt .Errorf ("error when reading response headers: %w" , err )
}
b = mustPeekBuffered (r )
headersLen , errParse := h .parse (b )
if errParse != nil {
return headerError ("response" , err , errParse , b , h .secureErrorLogMessage )
}
mustDiscard (r , headersLen )
return nil
}
func (h *ResponseHeader ) ReadTrailer (r *bufio .Reader ) error {
n := 1
for {
err := h .tryReadTrailer (r , n )
if err == nil {
return nil
}
if err != errNeedMore {
return err
}
n = r .Buffered () + 1
}
}
func (h *ResponseHeader ) tryReadTrailer (r *bufio .Reader , n int ) error {
b , err := r .Peek (n )
if len (b ) == 0 {
if x , ok := err .(interface { Timeout () bool }); ok && x .Timeout () {
return ErrTimeout
}
if n == 1 || err == io .EOF {
return io .EOF
}
if err == bufio .ErrBufferFull {
if h .secureErrorLogMessage {
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading response trailer" ),
}
}
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading response trailer: %w" , errSmallBuffer ),
}
}
return fmt .Errorf ("error when reading response trailer: %w" , err )
}
b = mustPeekBuffered (r )
headersLen , errParse := h .parseTrailer (b )
if errParse != nil {
if err == io .EOF {
return err
}
return headerError ("response" , err , errParse , b , h .secureErrorLogMessage )
}
mustDiscard (r , headersLen )
return nil
}
func headerError(typ string , err , errParse error , b []byte , secureErrorLogMessage bool ) error {
if errParse != errNeedMore {
return headerErrorMsg (typ , errParse , b , secureErrorLogMessage )
}
if err == nil {
return errNeedMore
}
if isOnlyCRLF (b ) {
return io .EOF
}
if err != bufio .ErrBufferFull {
return headerErrorMsg (typ , err , b , secureErrorLogMessage )
}
return &ErrSmallBuffer {
error : headerErrorMsg (typ , errSmallBuffer , b , secureErrorLogMessage ),
}
}
func headerErrorMsg(typ string , err error , b []byte , secureErrorLogMessage bool ) error {
if secureErrorLogMessage {
return fmt .Errorf ("error when reading %s headers: %w. Buffer size=%d" , typ , err , len (b ))
}
return fmt .Errorf ("error when reading %s headers: %w. Buffer size=%d, contents: %s" , typ , err , len (b ), bufferSnippet (b ))
}
func (h *RequestHeader ) Read (r *bufio .Reader ) error {
return h .readLoop (r , true )
}
func (h *RequestHeader ) readLoop (r *bufio .Reader , waitForMore bool ) error {
n := 1
for {
err := h .tryRead (r , n )
if err == nil {
return nil
}
if !waitForMore || err != errNeedMore {
h .resetSkipNormalize ()
return err
}
n = r .Buffered () + 1
}
}
func (h *RequestHeader ) ReadTrailer (r *bufio .Reader ) error {
n := 1
for {
err := h .tryReadTrailer (r , n )
if err == nil {
return nil
}
if err != errNeedMore {
return err
}
n = r .Buffered () + 1
}
}
func (h *RequestHeader ) tryReadTrailer (r *bufio .Reader , n int ) error {
b , err := r .Peek (n )
if len (b ) == 0 {
if x , ok := err .(interface { Timeout () bool }); ok && x .Timeout () {
return ErrTimeout
}
if n == 1 || err == io .EOF {
return io .EOF
}
if err == bufio .ErrBufferFull {
if h .secureErrorLogMessage {
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading request trailer" ),
}
}
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading request trailer: %w" , errSmallBuffer ),
}
}
return fmt .Errorf ("error when reading request trailer: %w" , err )
}
b = mustPeekBuffered (r )
headersLen , errParse := h .parseTrailer (b )
if errParse != nil {
if err == io .EOF {
return err
}
return headerError ("request" , err , errParse , b , h .secureErrorLogMessage )
}
mustDiscard (r , headersLen )
return nil
}
func (h *RequestHeader ) tryRead (r *bufio .Reader , n int ) error {
h .resetSkipNormalize ()
b , err := r .Peek (n )
if len (b ) == 0 {
if err == io .EOF {
return err
}
if err == nil {
panic ("bufio.Reader.Peek() returned nil, nil" )
}
if err == bufio .ErrBufferFull {
return &ErrSmallBuffer {
error : fmt .Errorf ("error when reading request headers: %w (n=%d, r.Buffered()=%d)" , errSmallBuffer , n , r .Buffered ()),
}
}
if n == 1 {
return ErrNothingRead {err }
}
return fmt .Errorf ("error when reading request headers: %w" , err )
}
b = mustPeekBuffered (r )
headersLen , errParse := h .parse (b )
if errParse != nil {
return headerError ("request" , err , errParse , b , h .secureErrorLogMessage )
}
mustDiscard (r , headersLen )
return nil
}
func bufferSnippet(b []byte ) string {
n := len (b )
start := 200
end := n - start
if start >= end {
start = n
end = n
}
bStart , bEnd := b [:start ], b [end :]
if len (bEnd ) == 0 {
return fmt .Sprintf ("%q" , b )
}
return fmt .Sprintf ("%q...%q" , bStart , bEnd )
}
func isOnlyCRLF(b []byte ) bool {
for _ , ch := range b {
if ch != rChar && ch != nChar {
return false
}
}
return true
}
func updateServerDate() {
refreshServerDate ()
go func () {
for {
time .Sleep (time .Second )
refreshServerDate ()
}
}()
}
var (
serverDate atomic .Value
serverDateOnce sync .Once
)
func refreshServerDate() {
b := AppendHTTPDate (nil , time .Now ())
serverDate .Store (b )
}
func (h *ResponseHeader ) Write (w *bufio .Writer ) error {
_ , err := w .Write (h .Header ())
return err
}
func (h *ResponseHeader ) WriteTo (w io .Writer ) (int64 , error ) {
n , err := w .Write (h .Header ())
return int64 (n ), err
}
func (h *ResponseHeader ) Header () []byte {
h .bufKV .value = h .AppendBytes (h .bufKV .value [:0 ])
return h .bufKV .value
}
func (h *ResponseHeader ) writeTrailer (w *bufio .Writer ) error {
_ , err := w .Write (h .TrailerHeader ())
return err
}
func (h *ResponseHeader ) TrailerHeader () []byte {
h .bufKV .value = h .bufKV .value [:0 ]
for _ , t := range h .trailer {
value := h .peek (t .key )
h .bufKV .value = appendHeaderLine (h .bufKV .value , t .key , value )
}
h .bufKV .value = append (h .bufKV .value , strCRLF ...)
return h .bufKV .value
}
func (h *ResponseHeader ) String () string {
return string (h .Header ())
}
func (h *ResponseHeader ) appendStatusLine (dst []byte ) []byte {
statusCode := h .StatusCode ()
if statusCode < 0 {
statusCode = StatusOK
}
return formatStatusLine (dst , h .Protocol (), statusCode , h .StatusMessage ())
}
func (h *ResponseHeader ) AppendBytes (dst []byte ) []byte {
dst = h .appendStatusLine (dst [:0 ])
server := h .Server ()
if len (server ) != 0 {
dst = appendHeaderLine (dst , strServer , server )
}
if !h .noDefaultDate {
serverDateOnce .Do (updateServerDate )
dst = appendHeaderLine (dst , strDate , serverDate .Load ().([]byte ))
}
if h .ContentLength () != 0 || len (h .contentType ) > 0 {
contentType := h .ContentType ()
if len (contentType ) > 0 {
dst = appendHeaderLine (dst , strContentType , contentType )
}
}
contentEncoding := h .ContentEncoding ()
if len (contentEncoding ) > 0 {
dst = appendHeaderLine (dst , strContentEncoding , contentEncoding )
}
if len (h .contentLengthBytes ) > 0 {
dst = appendHeaderLine (dst , strContentLength , h .contentLengthBytes )
}
for i , n := 0 , len (h .h ); i < n ; i ++ {
kv := &h .h [i ]
exclude := false
for _ , t := range h .trailer {
if bytes .Equal (kv .key , t .key ) {
exclude = true
break
}
}
if !exclude && (h .noDefaultDate || !bytes .Equal (kv .key , strDate )) {
dst = appendHeaderLine (dst , kv .key , kv .value )
}
}
if len (h .trailer ) > 0 {
dst = appendHeaderLine (dst , strTrailer , appendArgsKeyBytes (nil , h .trailer , strCommaSpace ))
}
n := len (h .cookies )
if n > 0 {
for i := 0 ; i < n ; i ++ {
kv := &h .cookies [i ]
dst = appendHeaderLine (dst , strSetCookie , kv .value )
}
}
if h .ConnectionClose () {
dst = appendHeaderLine (dst , strConnection , strClose )
}
return append (dst , strCRLF ...)
}
func (h *RequestHeader ) Write (w *bufio .Writer ) error {
_ , err := w .Write (h .Header ())
return err
}
func (h *RequestHeader ) WriteTo (w io .Writer ) (int64 , error ) {
n , err := w .Write (h .Header ())
return int64 (n ), err
}
func (h *RequestHeader ) Header () []byte {
h .bufKV .value = h .AppendBytes (h .bufKV .value [:0 ])
return h .bufKV .value
}
func (h *RequestHeader ) writeTrailer (w *bufio .Writer ) error {
_ , err := w .Write (h .TrailerHeader ())
return err
}
func (h *RequestHeader ) TrailerHeader () []byte {
h .bufKV .value = h .bufKV .value [:0 ]
for _ , t := range h .trailer {
value := h .peek (t .key )
h .bufKV .value = appendHeaderLine (h .bufKV .value , t .key , value )
}
h .bufKV .value = append (h .bufKV .value , strCRLF ...)
return h .bufKV .value
}
func (h *RequestHeader ) RawHeaders () []byte {
return h .rawHeaders
}
func (h *RequestHeader ) String () string {
return string (h .Header ())
}
func (h *RequestHeader ) AppendBytes (dst []byte ) []byte {
dst = append (dst , h .Method ()...)
dst = append (dst , ' ' )
dst = append (dst , h .RequestURI ()...)
dst = append (dst , ' ' )
dst = append (dst , h .Protocol ()...)
dst = append (dst , strCRLF ...)
userAgent := h .UserAgent ()
if len (userAgent ) > 0 && !h .disableSpecialHeader {
dst = appendHeaderLine (dst , strUserAgent , userAgent )
}
host := h .Host ()
if len (host ) > 0 && !h .disableSpecialHeader {
dst = appendHeaderLine (dst , strHost , host )
}
contentType := h .ContentType ()
if !h .noDefaultContentType && len (contentType ) == 0 && !h .ignoreBody () {
contentType = strDefaultContentType
}
if len (contentType ) > 0 && !h .disableSpecialHeader {
dst = appendHeaderLine (dst , strContentType , contentType )
}
if len (h .contentLengthBytes ) > 0 && !h .disableSpecialHeader {
dst = appendHeaderLine (dst , strContentLength , h .contentLengthBytes )
}
for i , n := 0 , len (h .h ); i < n ; i ++ {
kv := &h .h [i ]
exclude := false
for _ , t := range h .trailer {
if bytes .Equal (kv .key , t .key ) {
exclude = true
break
}
}
if !exclude {
dst = appendHeaderLine (dst , kv .key , kv .value )
}
}
if len (h .trailer ) > 0 {
dst = appendHeaderLine (dst , strTrailer , appendArgsKeyBytes (nil , h .trailer , strCommaSpace ))
}
n := len (h .cookies )
if n > 0 && !h .disableSpecialHeader {
dst = append (dst , strCookie ...)
dst = append (dst , strColonSpace ...)
dst = appendRequestCookieBytes (dst , h .cookies )
dst = append (dst , strCRLF ...)
}
if h .ConnectionClose () && !h .disableSpecialHeader {
dst = appendHeaderLine (dst , strConnection , strClose )
}
return append (dst , strCRLF ...)
}
func appendHeaderLine(dst , key , value []byte ) []byte {
dst = append (dst , key ...)
dst = append (dst , strColonSpace ...)
dst = append (dst , value ...)
return append (dst , strCRLF ...)
}
func (h *ResponseHeader ) parse (buf []byte ) (int , error ) {
m , err := h .parseFirstLine (buf )
if err != nil {
return 0 , err
}
n , err := h .parseHeaders (buf [m :])
if err != nil {
return 0 , err
}
return m + n , nil
}
func (h *ResponseHeader ) parseTrailer (buf []byte ) (int , error ) {
if buf [0 ] == '0' {
skip := len (strCRLF ) + 1
if len (buf ) < skip {
return 0 , io .EOF
}
buf = buf [skip :]
}
var s headerScanner
s .b = buf
s .disableNormalizing = h .disableNormalizing
var err error
for s .next () {
if len (s .key ) > 0 {
if bytes .IndexByte (s .key , ' ' ) != -1 || bytes .IndexByte (s .key , '\t' ) != -1 {
err = fmt .Errorf ("invalid trailer key %q" , s .key )
continue
}
if isBadTrailer (s .key ) {
err = fmt .Errorf ("forbidden trailer key %q" , s .key )
continue
}
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
}
}
if s .err != nil {
return 0 , s .err
}
if err != nil {
return 0 , err
}
return s .hLen , nil
}
func (h *RequestHeader ) ignoreBody () bool {
return h .IsGet () || h .IsHead ()
}
func (h *RequestHeader ) parse (buf []byte ) (int , error ) {
m , err := h .parseFirstLine (buf )
if err != nil {
return 0 , err
}
h .rawHeaders , _, err = readRawHeaders (h .rawHeaders [:0 ], buf [m :])
if err != nil {
return 0 , err
}
var n int
n , err = h .parseHeaders (buf [m :])
if err != nil {
return 0 , err
}
return m + n , nil
}
func (h *RequestHeader ) parseTrailer (buf []byte ) (int , error ) {
if buf [0 ] == '0' {
skip := len (strCRLF ) + 1
if len (buf ) < skip {
return 0 , io .EOF
}
buf = buf [skip :]
}
var s headerScanner
s .b = buf
s .disableNormalizing = h .disableNormalizing
var err error
for s .next () {
if len (s .key ) > 0 {
if bytes .IndexByte (s .key , ' ' ) != -1 || bytes .IndexByte (s .key , '\t' ) != -1 {
err = fmt .Errorf ("invalid trailer key %q" , s .key )
continue
}
if isBadTrailer (s .key ) {
err = fmt .Errorf ("forbidden trailer key %q" , s .key )
continue
}
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
}
}
if s .err != nil {
return 0 , s .err
}
if err != nil {
return 0 , err
}
return s .hLen , nil
}
func isBadTrailer(key []byte ) bool {
if len (key ) == 0 {
return true
}
switch key [0 ] | 0x20 {
case 'a' :
return caseInsensitiveCompare (key , strAuthorization )
case 'c' :
if len (key ) > len (HeaderContentType ) && caseInsensitiveCompare (key [:8 ], strContentType [:8 ]) {
return caseInsensitiveCompare (key [8 :], strContentEncoding [8 :]) ||
caseInsensitiveCompare (key [8 :], strContentLength [8 :]) ||
caseInsensitiveCompare (key [8 :], strContentType [8 :]) ||
caseInsensitiveCompare (key [8 :], strContentRange [8 :])
}
return caseInsensitiveCompare (key , strConnection )
case 'e' :
return caseInsensitiveCompare (key , strExpect )
case 'h' :
return caseInsensitiveCompare (key , strHost )
case 'k' :
return caseInsensitiveCompare (key , strKeepAlive )
case 'm' :
return caseInsensitiveCompare (key , strMaxForwards )
case 'p' :
if len (key ) > len (HeaderProxyConnection ) && caseInsensitiveCompare (key [:6 ], strProxyConnection [:6 ]) {
return caseInsensitiveCompare (key [6 :], strProxyConnection [6 :]) ||
caseInsensitiveCompare (key [6 :], strProxyAuthenticate [6 :]) ||
caseInsensitiveCompare (key [6 :], strProxyAuthorization [6 :])
}
case 'r' :
return caseInsensitiveCompare (key , strRange )
case 't' :
return caseInsensitiveCompare (key , strTE ) ||
caseInsensitiveCompare (key , strTrailer ) ||
caseInsensitiveCompare (key , strTransferEncoding )
case 'w' :
return caseInsensitiveCompare (key , strWWWAuthenticate )
}
return false
}
func (h *ResponseHeader ) parseFirstLine (buf []byte ) (int , error ) {
bNext := buf
var b []byte
var err error
for len (b ) == 0 {
if b , bNext , err = nextLine (bNext ); err != nil {
return 0 , err
}
}
n := bytes .IndexByte (b , ' ' )
if n < 0 {
if h .secureErrorLogMessage {
return 0 , fmt .Errorf ("cannot find whitespace in the first line of response" )
}
return 0 , fmt .Errorf ("cannot find whitespace in the first line of response %q" , buf )
}
h .noHTTP11 = !bytes .Equal (b [:n ], strHTTP11 )
b = b [n +1 :]
h .statusCode , n , err = parseUintBuf (b )
if err != nil {
if h .secureErrorLogMessage {
return 0 , fmt .Errorf ("cannot parse response status code: %w" , err )
}
return 0 , fmt .Errorf ("cannot parse response status code: %w. Response %q" , err , buf )
}
if len (b ) > n && b [n ] != ' ' {
if h .secureErrorLogMessage {
return 0 , fmt .Errorf ("unexpected char at the end of status code" )
}
return 0 , fmt .Errorf ("unexpected char at the end of status code. Response %q" , buf )
}
if len (b ) > n +1 {
h .SetStatusMessage (b [n +1 :])
}
return len (buf ) - len (bNext ), nil
}
func (h *RequestHeader ) parseFirstLine (buf []byte ) (int , error ) {
bNext := buf
var b []byte
var err error
for len (b ) == 0 {
if b , bNext , err = nextLine (bNext ); err != nil {
return 0 , err
}
}
n := bytes .IndexByte (b , ' ' )
if n <= 0 {
if h .secureErrorLogMessage {
return 0 , fmt .Errorf ("cannot find http request method" )
}
return 0 , fmt .Errorf ("cannot find http request method in %q" , buf )
}
h .method = append (h .method [:0 ], b [:n ]...)
b = b [n +1 :]
protoStr := strHTTP11
n = bytes .LastIndexByte (b , ' ' )
switch {
case n < 0 :
h .noHTTP11 = true
n = len (b )
protoStr = strHTTP10
case n == 0 :
if h .secureErrorLogMessage {
return 0 , fmt .Errorf ("requestURI cannot be empty" )
}
return 0 , fmt .Errorf ("requestURI cannot be empty in %q" , buf )
case !bytes .Equal (b [n +1 :], strHTTP11 ):
h .noHTTP11 = true
protoStr = b [n +1 :]
}
h .proto = append (h .proto [:0 ], protoStr ...)
h .requestURI = append (h .requestURI [:0 ], b [:n ]...)
return len (buf ) - len (bNext ), nil
}
func readRawHeaders(dst , buf []byte ) ([]byte , int , error ) {
n := bytes .IndexByte (buf , nChar )
if n < 0 {
return dst [:0 ], 0 , errNeedMore
}
if (n == 1 && buf [0 ] == rChar ) || n == 0 {
return dst , n + 1 , nil
}
n ++
b := buf
m := n
for {
b = b [m :]
m = bytes .IndexByte (b , nChar )
if m < 0 {
return dst , 0 , errNeedMore
}
m ++
n += m
if (m == 2 && b [0 ] == rChar ) || m == 1 {
dst = append (dst , buf [:n ]...)
return dst , n , nil
}
}
}
func (h *ResponseHeader ) parseHeaders (buf []byte ) (int , error ) {
h .contentLength = -2
var s headerScanner
s .b = buf
s .disableNormalizing = h .disableNormalizing
var err error
var kv *argsKV
for s .next () {
if len (s .key ) > 0 {
switch s .key [0 ] | 0x20 {
case 'c' :
if caseInsensitiveCompare (s .key , strContentType ) {
h .contentType = append (h .contentType [:0 ], s .value ...)
continue
}
if caseInsensitiveCompare (s .key , strContentEncoding ) {
h .contentEncoding = append (h .contentEncoding [:0 ], s .value ...)
continue
}
if caseInsensitiveCompare (s .key , strContentLength ) {
if h .contentLength != -1 {
if h .contentLength , err = parseContentLength (s .value ); err != nil {
h .contentLength = -2
} else {
h .contentLengthBytes = append (h .contentLengthBytes [:0 ], s .value ...)
}
}
continue
}
if caseInsensitiveCompare (s .key , strConnection ) {
if bytes .Equal (s .value , strClose ) {
h .connectionClose = true
} else {
h .connectionClose = false
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
}
continue
}
case 's' :
if caseInsensitiveCompare (s .key , strServer ) {
h .server = append (h .server [:0 ], s .value ...)
continue
}
if caseInsensitiveCompare (s .key , strSetCookie ) {
h .cookies , kv = allocArg (h .cookies )
kv .key = getCookieKey (kv .key , s .value )
kv .value = append (kv .value [:0 ], s .value ...)
continue
}
case 't' :
if caseInsensitiveCompare (s .key , strTransferEncoding ) {
if len (s .value ) > 0 && !bytes .Equal (s .value , strIdentity ) {
h .contentLength = -1
h .h = setArgBytes (h .h , strTransferEncoding , strChunked , argsHasValue )
}
continue
}
if caseInsensitiveCompare (s .key , strTrailer ) {
err = h .SetTrailerBytes (s .value )
continue
}
}
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
}
}
if s .err != nil {
h .connectionClose = true
return 0 , s .err
}
if h .contentLength < 0 {
h .contentLengthBytes = h .contentLengthBytes [:0 ]
}
if h .contentLength == -2 && !h .ConnectionUpgrade () && !h .mustSkipContentLength () {
h .h = setArgBytes (h .h , strTransferEncoding , strIdentity , argsHasValue )
h .connectionClose = true
}
if h .noHTTP11 && !h .connectionClose {
v := peekArgBytes (h .h , strConnection )
h .connectionClose = !hasHeaderValue (v , strKeepAlive )
}
return len (buf ) - len (s .b ), err
}
func (h *RequestHeader ) parseHeaders (buf []byte ) (int , error ) {
h .contentLength = -2
var s headerScanner
s .b = buf
s .disableNormalizing = h .disableNormalizing
var err error
for s .next () {
if len (s .key ) > 0 {
if bytes .IndexByte (s .key , ' ' ) != -1 || bytes .IndexByte (s .key , '\t' ) != -1 {
err = fmt .Errorf ("invalid header key %q" , s .key )
continue
}
if h .disableSpecialHeader {
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
continue
}
switch s .key [0 ] | 0x20 {
case 'h' :
if caseInsensitiveCompare (s .key , strHost ) {
h .host = append (h .host [:0 ], s .value ...)
continue
}
case 'u' :
if caseInsensitiveCompare (s .key , strUserAgent ) {
h .userAgent = append (h .userAgent [:0 ], s .value ...)
continue
}
case 'c' :
if caseInsensitiveCompare (s .key , strContentType ) {
h .contentType = append (h .contentType [:0 ], s .value ...)
continue
}
if caseInsensitiveCompare (s .key , strContentLength ) {
if h .contentLength != -1 {
var nerr error
if h .contentLength , nerr = parseContentLength (s .value ); nerr != nil {
if err == nil {
err = nerr
}
h .contentLength = -2
} else {
h .contentLengthBytes = append (h .contentLengthBytes [:0 ], s .value ...)
}
}
continue
}
if caseInsensitiveCompare (s .key , strConnection ) {
if bytes .Equal (s .value , strClose ) {
h .connectionClose = true
} else {
h .connectionClose = false
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
}
continue
}
case 't' :
if caseInsensitiveCompare (s .key , strTransferEncoding ) {
if !bytes .Equal (s .value , strIdentity ) {
h .contentLength = -1
h .h = setArgBytes (h .h , strTransferEncoding , strChunked , argsHasValue )
}
continue
}
if caseInsensitiveCompare (s .key , strTrailer ) {
if nerr := h .SetTrailerBytes (s .value ); nerr != nil {
if err == nil {
err = nerr
}
}
continue
}
}
}
h .h = appendArgBytes (h .h , s .key , s .value , argsHasValue )
}
if s .err != nil && err == nil {
err = s .err
}
if err != nil {
h .connectionClose = true
return 0 , err
}
if h .contentLength < 0 {
h .contentLengthBytes = h .contentLengthBytes [:0 ]
}
if h .noHTTP11 && !h .connectionClose {
v := peekArgBytes (h .h , strConnection )
h .connectionClose = !hasHeaderValue (v , strKeepAlive )
}
return s .hLen , nil
}
func (h *RequestHeader ) collectCookies () {
if h .cookiesCollected {
return
}
for i , n := 0 , len (h .h ); i < n ; i ++ {
kv := &h .h [i ]
if caseInsensitiveCompare (kv .key , strCookie ) {
h .cookies = parseRequestCookies (h .cookies , kv .value )
tmp := *kv
copy (h .h [i :], h .h [i +1 :])
n --
i --
h .h [n ] = tmp
h .h = h .h [:n ]
}
}
h .cookiesCollected = true
}
var errNonNumericChars = errors .New ("non-numeric chars found" )
func parseContentLength(b []byte ) (int , error ) {
v , n , err := parseUintBuf (b )
if err != nil {
return -1 , fmt .Errorf ("cannot parse Content-Length: %w" , err )
}
if n != len (b ) {
return -1 , fmt .Errorf ("cannot parse Content-Length: %w" , errNonNumericChars )
}
return v , nil
}
type headerScanner struct {
b []byte
key []byte
value []byte
err error
hLen int
disableNormalizing bool
nextColon int
nextNewLine int
initialized bool
}
func (s *headerScanner ) next () bool {
if !s .initialized {
s .nextColon = -1
s .nextNewLine = -1
s .initialized = true
}
bLen := len (s .b )
if bLen >= 2 && s .b [0 ] == rChar && s .b [1 ] == nChar {
s .b = s .b [2 :]
s .hLen += 2
return false
}
if bLen >= 1 && s .b [0 ] == nChar {
s .b = s .b [1 :]
s .hLen ++
return false
}
var n int
if s .nextColon >= 0 {
n = s .nextColon
s .nextColon = -1
} else {
n = bytes .IndexByte (s .b , ':' )
x := bytes .IndexByte (s .b , nChar )
if x < 0 {
s .err = errNeedMore
return false
}
if x < n {
s .err = errInvalidName
return false
}
}
if n < 0 {
s .err = errNeedMore
return false
}
s .key = s .b [:n ]
normalizeHeaderKey (s .key , s .disableNormalizing )
n ++
for len (s .b ) > n && s .b [n ] == ' ' {
n ++
s .nextNewLine --
}
s .hLen += n
s .b = s .b [n :]
if s .nextNewLine >= 0 {
n = s .nextNewLine
s .nextNewLine = -1
} else {
n = bytes .IndexByte (s .b , nChar )
}
if n < 0 {
s .err = errNeedMore
return false
}
isMultiLineValue := false
for {
if n +1 >= len (s .b ) {
break
}
if s .b [n +1 ] != ' ' && s .b [n +1 ] != '\t' {
break
}
d := bytes .IndexByte (s .b [n +1 :], nChar )
if d <= 0 {
break
} else if d == 1 && s .b [n +1 ] == rChar {
break
}
e := n + d + 1
if c := bytes .IndexByte (s .b [n +1 :e ], ':' ); c >= 0 {
s .nextColon = c
s .nextNewLine = d - c - 1
break
}
isMultiLineValue = true
n = e
}
if n >= len (s .b ) {
s .err = errNeedMore
return false
}
oldB := s .b
s .value = s .b [:n ]
s .hLen += n + 1
s .b = s .b [n +1 :]
if n > 0 && s .value [n -1 ] == rChar {
n --
}
for n > 0 && s .value [n -1 ] == ' ' {
n --
}
s .value = s .value [:n ]
if isMultiLineValue {
s .value , s .b , s .hLen = normalizeHeaderValue (s .value , oldB , s .hLen )
}
return true
}
type headerValueScanner struct {
b []byte
value []byte
}
func (s *headerValueScanner ) next () bool {
b := s .b
if len (b ) == 0 {
return false
}
n := bytes .IndexByte (b , ',' )
if n < 0 {
s .value = stripSpace (b )
s .b = b [len (b ):]
return true
}
s .value = stripSpace (b [:n ])
s .b = b [n +1 :]
return true
}
func stripSpace(b []byte ) []byte {
for len (b ) > 0 && b [0 ] == ' ' {
b = b [1 :]
}
for len (b ) > 0 && b [len (b )-1 ] == ' ' {
b = b [:len (b )-1 ]
}
return b
}
func hasHeaderValue(s , value []byte ) bool {
var vs headerValueScanner
vs .b = s
for vs .next () {
if caseInsensitiveCompare (vs .value , value ) {
return true
}
}
return false
}
func nextLine(b []byte ) ([]byte , []byte , error ) {
nNext := bytes .IndexByte (b , nChar )
if nNext < 0 {
return nil , nil , errNeedMore
}
n := nNext
if n > 0 && b [n -1 ] == rChar {
n --
}
return b [:n ], b [nNext +1 :], nil
}
func initHeaderKV(kv *argsKV , key , value string , disableNormalizing bool ) {
kv .key = getHeaderKeyBytes (kv , key , disableNormalizing )
kv .value = append (kv .value [:0 ], value ...)
kv .value = removeNewLines (kv .value )
}
func getHeaderKeyBytes(kv *argsKV , key string , disableNormalizing bool ) []byte {
kv .key = append (kv .key [:0 ], key ...)
normalizeHeaderKey (kv .key , disableNormalizing )
return kv .key
}
func normalizeHeaderValue(ov , ob []byte , headerLength int ) (nv , nb []byte , nhl int ) {
nv = ov
length := len (ov )
if length <= 0 {
return
}
write := 0
shrunk := 0
lineStart := false
for read := 0 ; read < length ; read ++ {
c := ov [read ]
switch {
case c == rChar || c == nChar :
shrunk ++
if c == nChar {
lineStart = true
}
continue
case lineStart && c == '\t' :
c = ' '
default :
lineStart = false
}
nv [write ] = c
write ++
}
nv = nv [:write ]
copy (ob [write :], ob [write +shrunk :])
skip := 0
if ob [write ] == rChar {
if ob [write +1 ] == nChar {
skip += 2
} else {
skip ++
}
} else if ob [write ] == nChar {
skip ++
}
nb = ob [write +skip : len (ob )-shrunk ]
nhl = headerLength - shrunk
return
}
func normalizeHeaderKey(b []byte , disableNormalizing bool ) {
if disableNormalizing {
return
}
n := len (b )
if n == 0 {
return
}
b [0 ] = toUpperTable [b [0 ]]
for i := 1 ; i < n ; i ++ {
p := &b [i ]
if *p == '-' {
i ++
if i < n {
b [i ] = toUpperTable [b [i ]]
}
continue
}
*p = toLowerTable [*p ]
}
}
func removeNewLines(raw []byte ) []byte {
foundR := bytes .IndexByte (raw , rChar )
foundN := bytes .IndexByte (raw , nChar )
start := 0
switch {
case foundN != -1 :
if foundR > foundN {
start = foundN
} else if foundR != -1 {
start = foundR
}
case foundR != -1 :
start = foundR
default :
return raw
}
for i := start ; i < len (raw ); i ++ {
switch raw [i ] {
case rChar , nChar :
raw [i ] = ' '
default :
continue
}
}
return raw
}
func AppendNormalizedHeaderKey (dst []byte , key string ) []byte {
dst = append (dst , key ...)
normalizeHeaderKey (dst [len (dst )-len (key ):], false )
return dst
}
func AppendNormalizedHeaderKeyBytes (dst , key []byte ) []byte {
return AppendNormalizedHeaderKey (dst , b2s (key ))
}
func appendArgsKeyBytes(dst []byte , args []argsKV , sep []byte ) []byte {
for i , n := 0 , len (args ); i < n ; i ++ {
kv := &args [i ]
dst = append (dst , kv .key ...)
if i +1 < n {
dst = append (dst , sep ...)
}
}
return dst
}
var (
errNeedMore = errors .New ("need more data: cannot find trailing lf" )
errInvalidName = errors .New ("invalid header name" )
errSmallBuffer = errors .New ("small read buffer. Increase ReadBufferSize" )
)
type ErrNothingRead struct {
error
}
type ErrSmallBuffer struct {
error
}
func mustPeekBuffered(r *bufio .Reader ) []byte {
buf , err := r .Peek (r .Buffered ())
if len (buf ) == 0 || err != nil {
panic (fmt .Sprintf ("bufio.Reader.Peek() returned unexpected data (%q, %v)" , buf , err ))
}
return buf
}
func mustDiscard(r *bufio .Reader , n int ) {
if _ , err := r .Discard (n ); err != nil {
panic (fmt .Sprintf ("bufio.Reader.Discard(%d) failed: %v" , n , err ))
}
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .