github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/http/header-cookie.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 "github.com/GeniusesGroup/libgo/convert" 9 "github.com/GeniusesGroup/libgo/protocol" 10 ) 11 12 type Cookies string 13 14 // Cookies parses and returns the Cookie headers. 15 // By related RFC we just support one Cookie in header. 16 // https://tools.ietf.org/html/rfc6265#section-5.4 17 func (c Cookies) Next() (cookie Cookie, remain Cookies) { 18 var rawRemain, _ = cookie.UnmarshalFrom(string(c)) 19 remain = Cookies(rawRemain) 20 return 21 } 22 23 // Cookies parses and returns the Cookie headers. 24 // By related RFC we just support one Cookie in header. 25 // https://tools.ietf.org/html/rfc6265#section-5.4 26 func (h *header) Cookies() (cookies []Cookie) { 27 var cookie = h.Get(HeaderKeyCookie) 28 if len(cookie) == 0 { 29 return 30 } 31 var index int 32 cookies = make([]Cookie, 0, 8) 33 var c Cookie 34 for { 35 index = strings.IndexByte(cookie, ';') 36 if index == -1 { 37 c.Unmarshal(cookie) 38 cookies = append(cookies, c) 39 return 40 } 41 c.Unmarshal(cookie[:index]) 42 cookies = append(cookies, c) 43 44 cookie = cookie[index+2:] 45 } 46 } 47 48 // MarshalCookies parses and set them to the Cookie header. 49 func (h *header) MarshalCookies(cookies []Cookie) { 50 // TODO::: make buffer by needed size. 51 var b strings.Builder 52 var ln = len(cookies) 53 var i int 54 for ; ; i++ { 55 b.WriteString(cookies[i].Name) 56 b.WriteByte('=') 57 b.WriteString(cookies[i].Value) 58 if i < ln { 59 b.WriteString(SemiColonSpace) 60 } else { 61 break 62 } 63 } 64 h.Set(HeaderKeyCookie, b.String()) 65 } 66 67 // Cookie represents an HTTP cookie as sent in the Cookie header of an HTTP request. 68 // implement by https://tools.ietf.org/html/rfc6265#section-4.2 69 type Cookie struct { 70 Name string 71 Value string 72 } 73 74 // CheckAndSanitize check if the cookie is in standard by RFC and try to fix them. It returns last error! 75 func (c *Cookie) CheckAndSanitize() (err protocol.Error) { 76 c.Name, err = sanitizeCookieName(c.Name) 77 if err != nil { 78 return 79 } 80 c.Value, err = sanitizeCookieValue(c.Value) 81 return 82 } 83 84 // Marshal returns the serialization of the cookie. 85 func (c *Cookie) Marshal() string { 86 return c.Name + "=" + c.Value 87 } 88 89 // Unmarshal parse given cookie value to c and return 90 func (c *Cookie) Unmarshal(cookie string) { 91 var equalIndex = strings.IndexByte(cookie, '=') 92 // First check no equal(=) sign or empty name or value 93 if equalIndex < 1 || equalIndex == len(cookie)-1 { 94 return 95 } 96 c.Name = cookie[:equalIndex] 97 c.Value = cookie[equalIndex+1:] 98 } 99 100 // Unmarshal parse given cookies value to c and return 101 func (c *Cookie) UnmarshalFrom(cookies string) (remain string, err protocol.Error) { 102 var equalIndex = strings.IndexByte(cookies, '=') 103 // First check no equal(=) sign or empty name or value 104 if equalIndex < 1 || equalIndex == len(cookies)-1 { 105 // err = 106 return 107 } 108 c.Name = cookies[:equalIndex] 109 110 var SemiColonIndex = strings.IndexByte(cookies, ';') 111 if SemiColonIndex == -1 { 112 c.Value = cookies[equalIndex+1:] 113 } else { 114 c.Value = cookies[equalIndex+1 : SemiColonIndex] 115 remain = cookies[SemiColonIndex+2:] // Due to have space after semi colon do +2 116 } 117 return 118 } 119 120 func sanitizeCookieName(n string) (name string, err protocol.Error) { 121 var ln = len(n) 122 var buf = make([]byte, 0, ln) 123 var b byte 124 for i := 0; i < ln; i++ { 125 b = n[i] 126 if b == '\n' || b == '\r' { 127 buf = append(buf, '-') 128 err = &ErrCookieBadName 129 } else { 130 buf = append(buf, b) 131 } 132 } 133 name = convert.UnsafeByteSliceToString(buf) 134 return 135 } 136 137 // https://tools.ietf.org/html/rfc6265#section-4.1.1 138 // cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) 139 // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E 140 // ; US-ASCII characters excluding CTLs, 141 // ; whitespace, DQUOTE, comma, semicolon, 142 // ; and backslash 143 // Don't check for ; due to Unmarshal will panic for bad cookie!! 144 func sanitizeCookieValue(v string) (value string, err protocol.Error) { 145 var ln = len(v) 146 var buf = make([]byte, 0, ln) 147 var b byte 148 for i := 0; i < ln; i++ { 149 b = v[i] 150 if 0x20 <= b && b < 0x7f && b != ' ' && b != '"' && b != ',' && b != '\\' { 151 buf = append(buf, b) 152 } else { 153 err = &ErrCookieBadValue 154 } 155 } 156 value = convert.UnsafeByteSliceToString(buf) 157 return 158 }