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