github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/http/header.go (about) 1 /* For license and copyright information please see the LEGAL file in the code repository */ 2 3 package http 4 5 import ( 6 "strings" 7 8 "golang.org/x/exp/maps" 9 10 "github.com/GeniusesGroup/libgo/convert" 11 "github.com/GeniusesGroup/libgo/protocol" 12 ) 13 14 // header is represent HTTP header structure! 15 type header struct { 16 headers map[string][]string 17 valuesPool []string // shared backing array for headers' values 18 } 19 20 func (h *header) Init() { 21 h.headers = make(map[string][]string, headerInitMapLen) 22 h.valuesPool = make([]string, headerValuesPoolLen) 23 } 24 func (h *header) Reinit() { 25 maps.Clear(h.headers) 26 h.valuesPool = h.valuesPool[:0] 27 } 28 func (h *header) Deinit() {} 29 30 // Get returns the first value associated with the given key. 31 // Key must already be in CanonicalHeaderKey form. 32 func (h *header) Get(key string) string { 33 var values = h.headers[key] 34 if len(values) > 0 { 35 return values[0] 36 } 37 return "" 38 } 39 40 // Gets returns all values associated with the given key. 41 // Key must already be in CanonicalHeaderKey form. 42 func (h *header) Gets(key string) []string { 43 var values = h.headers[key] 44 if len(values) > 0 { 45 return values 46 } 47 return nil 48 } 49 50 // Add adds the key, value pair to the header. 51 // It appends to any existing values associated with key. 52 // Key must already be in CanonicalHeaderKey form. 53 func (h *header) Add(key, value string) { 54 var values []string = h.headers[key] 55 if values == nil { 56 h.Set(key, value) 57 } else { 58 h.headers[key] = append(values, value) 59 } 60 } 61 62 // Adds append given values to end of given key exiting values. 63 // Key must already be in CanonicalHeaderKey form. 64 func (h *header) Adds(key string, values []string) { 65 h.headers[key] = append(h.headers[key], values...) 66 } 67 68 // Set replace given value in given key. 69 // Key must already be in CanonicalHeaderKey form. 70 func (h *header) Set(key string, value string) { 71 if len(h.valuesPool) == 0 { 72 h.valuesPool = make([]string, headerValuesPoolLen) 73 } 74 // More than likely this will be a single-element key. Most headers aren't multi-valued. 75 // Set the capacity on valuesPool[0] to 1, so any future append won't extend the slice into the other strings. 76 var values []string = h.valuesPool[:1:1] 77 h.valuesPool = h.valuesPool[1:] 78 values[0] = value 79 h.headers[key] = values 80 } 81 82 // Sets sets the header entries associated with key to 83 // the single element value. It replaces any existing values associated with key. 84 // Key must already be in CanonicalHeaderKey form. 85 func (h *header) Sets(key string, values []string) { 86 h.headers[key] = values 87 } 88 89 // Replace change the exiting header entry associated with key to the given single element value. 90 // It use for some logic like TransferEncoding(), ... 91 func (h *header) Replace(key string, value string) { 92 h.headers[key][0] = value 93 } 94 95 // Del deletes the values associated with key. 96 // Key must already be in CanonicalHeaderKey form. 97 func (h *header) Del(key string) { 98 delete(h.headers, key) 99 } 100 101 // Exclude eliminate headers by given keys! 102 func (h *header) Exclude(exclude map[string]bool) { 103 for key := range exclude { 104 delete(h.headers, key) 105 } 106 } 107 108 //libgo:impl protocol.Codec 109 func (h *header) Decode(source protocol.Codec) (n int, err protocol.Error) { 110 // TODO::: 111 return 112 } 113 func (h *header) Encode(destination protocol.Codec) (n int, err protocol.Error) { 114 var encodedHeader = h.Marshal() 115 n, err = destination.Unmarshal(encodedHeader) 116 return 117 } 118 func (h *header) Marshal() (httpHeader []byte) { 119 httpHeader = make([]byte, 0, h.Len()) 120 httpHeader = h.MarshalTo(httpHeader) 121 return 122 } 123 func (h *header) MarshalTo(httpPacket []byte) []byte { 124 for key, values := range h.headers { 125 // TODO::: some header key must not inline by coma like set-cookie. check if other need this exception. 126 switch key { 127 case HeaderKeySetCookie: 128 for _, value := range values { 129 httpPacket = append(httpPacket, key...) 130 httpPacket = append(httpPacket, ColonSpace...) 131 httpPacket = append(httpPacket, value...) 132 httpPacket = append(httpPacket, CRLF...) 133 } 134 default: 135 httpPacket = append(httpPacket, key...) 136 httpPacket = append(httpPacket, ColonSpace...) 137 for _, value := range values { 138 httpPacket = append(httpPacket, value...) 139 httpPacket = append(httpPacket, Comma) 140 } 141 httpPacket = httpPacket[:len(httpPacket)-1] // Remove trailing comma 142 httpPacket = append(httpPacket, CRLF...) 143 } 144 } 145 return httpPacket 146 } 147 func (h *header) Unmarshal(data []byte) (n int, err protocol.Error) { 148 var s = convert.UnsafeByteSliceToString(data) 149 n, err = h.unmarshal(s) 150 return 151 } 152 func (h *header) Len() (ln int) { 153 for key, values := range h.headers { 154 ln += len(key) 155 ln += 4 // 4=len(ColonSpace)+len(CRLF) 156 for _, value := range values { 157 ln += len(value) 158 ln++ // 1=len(Coma) 159 } 160 } 161 return 162 } 163 164 /* 165 ********** Other methods ********** 166 */ 167 168 // unmarshal parses and decodes data of given httpPacket(without first line) to (h *header). 169 // This method not respect to some RFCs like field-name in RFC7230, ... due to be more liberal in what it accept! 170 // In some bad packet may occur panic, handle panic by recover otherwise app will crash and exit! 171 func (h *header) unmarshal(s string) (headerEnd int, err protocol.Error) { 172 var colonIndex, newLineIndex int 173 var key, value string 174 for { 175 newLineIndex = strings.IndexByte(s, '\r') 176 if newLineIndex < 3 { 177 // newLineIndex == -1 >> broken or malformed packet, panic may occur! 178 // newLineIndex == 0 >> End of headers part of packet, no panic 179 // 1 < newLineIndex > 3 >> bad header || broken || malformed packet, panic may occur! 180 return 181 } 182 183 colonIndex = strings.IndexByte(s[:newLineIndex], ':') 184 switch colonIndex { 185 case -1: 186 // TODO::: Header key without value!?? Bad http packet!?? 187 default: 188 key = s[:colonIndex] 189 value = s[colonIndex+2 : newLineIndex] // +2 due to have a space after colon force by RFC && 190 h.Add(key, value) // TODO::: is legal to have multiple key in request header or use h.Set()?? 191 } 192 newLineIndex += 2 // +2 due to have "\r\n" at end of each header line 193 s = s[newLineIndex:] 194 headerEnd += newLineIndex 195 } 196 }