github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/http/uri.go (about) 1 /* For license and copyright information please see LEGAL file in repository */ 2 3 package http 4 5 import ( 6 "io" 7 8 "../convert" 9 "../mediatype" 10 "../protocol" 11 ) 12 13 // URI store http URI parts. 14 // https://tools.ietf.org/html/rfc2616#section-3.2 15 // https://tools.ietf.org/html/rfc2616#section-5.1.2 16 // https://tools.ietf.org/html/rfc3986 17 // Request-URI = "*" | absoluteURI | abs_path | authority 18 // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]] 19 type URI struct { 20 uri string 21 uriAsByte []byte 22 scheme string // = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) 23 authority string // host [ ":" port ] 24 host string // host without port if any exist in authority 25 path string // 26 query string // encoded query values, without '?' 27 fragment string // fragment for references, without '#' 28 } 29 30 func (u *URI) Init(uri string) { u.unmarshalFrom(uri) } 31 func (u *URI) Reset() { 32 u.uri = "" 33 u.uriAsByte = []byte{} 34 u.scheme = "" 35 u.authority = "" 36 u.host = "" 37 u.path = "" 38 u.query = "" 39 u.fragment = "" 40 } 41 func (u *URI) Set(scheme, authority, path, query string) { 42 u.scheme, u.authority, u.path, u.query = scheme, authority, path, query 43 } 44 45 func (u *URI) URI() string { return u.uri } 46 func (u *URI) Scheme() string { return u.scheme } 47 func (u *URI) Authority() string { return u.authority } 48 func (u *URI) Host() string { return u.host } 49 func (u *URI) Path() string { return u.path } 50 func (u *URI) Query() string { return u.query } 51 func (u *URI) Fragment() string { return u.fragment } 52 53 // checkHost check host of request by RFC 7230, section 5.3 rules: Must treat 54 // GET / HTTP/1.1 55 // Host: www.sabz.city 56 // and 57 // GET https://www.sabz.city/ HTTP/1.1 58 // Host: apis.sabz.city 59 // the same. In the second case, any Host line is ignored. 60 func (u *URI) checkHost(h *header) { 61 if u.authority == "" { 62 u.host = h.Get(HeaderKeyHost) 63 } 64 // TODO::: decode host (remove port if exist) from authority 65 u.host = u.authority 66 } 67 68 /* 69 ********** protocol.Codec interface ********** 70 */ 71 72 func (u *URI) MediaType() protocol.MediaType { return mediatype.URI } // application/x-www-form-urlencoded 73 func (u *URI) CompressType() protocol.CompressType { return nil } 74 func (u *URI) Len() (ln int) { 75 ln = len(u.uriAsByte) 76 if ln == 0 { 77 ln = u.len() 78 } 79 return 80 } 81 82 func (u *URI) Decode(reader protocol.Reader) (err protocol.Error) { 83 // TODO::: 84 return 85 } 86 87 func (u *URI) Encode(writer protocol.Writer) (err protocol.Error) { 88 var encodedURI = u.Marshal() 89 var _, goErr = writer.Write(encodedURI) 90 if goErr != nil { 91 // err = 92 } 93 return 94 } 95 96 // Marshal encode URI data and return it. 97 func (u *URI) Marshal() (encodedURI []byte) { 98 if u.uriAsByte == nil { 99 u.uriAsByte = make([]byte, 0, u.len()) 100 u.marshalTo(u.uriAsByte) 101 } 102 return u.uriAsByte 103 } 104 105 // MarshalTo encode URI data to given httpPacket and update u.uri and return httpPacket with new len. 106 func (u *URI) MarshalTo(httpPacket []byte) []byte { 107 if u.uriAsByte == nil { 108 return u.marshalTo(httpPacket) 109 } 110 return append(httpPacket, u.uriAsByte...) 111 } 112 113 // Unmarshal use to parse and decode given URI to u 114 func (u *URI) Unmarshal(uri []byte) (err protocol.Error) { 115 u.uri = convert.UnsafeByteSliceToString(uri) 116 u.uriAsByte = uri 117 u.unmarshalFrom(u.uri) 118 return 119 } 120 121 // UnmarshalFrom use to parse and decode given URI to u 122 func (u *URI) UnmarshalFrom(data []byte) (remaining []byte, err protocol.Error) { 123 var uriEnd = u.unmarshalFrom(convert.UnsafeByteSliceToString(data)) 124 remaining = data[uriEnd:] 125 return 126 } 127 128 /* 129 ********** io package interfaces ********** 130 */ 131 132 func (u *URI) WriteTo(writer io.Writer) (n int64, err error) { 133 var encodedURI = u.Marshal() 134 var writeLength int 135 writeLength, err = writer.Write(encodedURI) 136 n = int64(writeLength) 137 return 138 } 139 140 /* 141 ********** local methods ********** 142 */ 143 144 func (u *URI) marshalTo(httpPacket []byte) []byte { 145 var uriStart = len(httpPacket) 146 if u.scheme != "" { 147 httpPacket = append(httpPacket, u.scheme...) 148 httpPacket = append(httpPacket, "://"...) 149 } 150 httpPacket = append(httpPacket, u.authority...) 151 if u.path == "" { 152 httpPacket = append(httpPacket, Slash) 153 } else { 154 httpPacket = append(httpPacket, u.path...) 155 } 156 if u.query != "" { 157 httpPacket = append(httpPacket, Question) 158 httpPacket = append(httpPacket, u.query...) 159 } 160 161 // TODO::: below code cause memory leak if dev use u.uriAsByte||u.uri in other places due to GC can't free whole http packet 162 u.uriAsByte = httpPacket[uriStart:] 163 u.uri = convert.UnsafeByteSliceToString(u.uriAsByte) 164 return httpPacket 165 } 166 167 // unmarshalFrom use to parse and decode given URI to u 168 func (u *URI) unmarshalFrom(s string) (uriEnd int) { 169 if s[0] == Asterisk { 170 uriEnd = 1 171 } else { 172 var originForm bool 173 if s[0] == '/' { 174 originForm = true 175 } 176 177 var authorityStartIndex, pathStartIndex, questionIndex, numberSignIndex int 178 var ln = len(s) 179 var i int 180 Loop: 181 for i = 0; i < ln; i++ { 182 switch s[i] { 183 case Colon: 184 // Check : mark is first appear before any start||end sign or it is part of others! 185 if authorityStartIndex == 0 { 186 u.scheme = s[:i] 187 i += 2 // next loop will i+=1 so we just add i+=2 188 authorityStartIndex = i + 1 // +3 due to have :// 189 } 190 case Slash: 191 // Just check slash in middle of URI! If URI in origin form pathStartIndex always be 0! 192 if authorityStartIndex != 0 && pathStartIndex == 0 { 193 pathStartIndex = i 194 u.authority = s[authorityStartIndex:pathStartIndex] 195 } else if !originForm && pathStartIndex == 0 && i != 0 { 196 pathStartIndex = i 197 u.authority = s[:i] 198 } 199 case Question: 200 // Check ? mark is first appear or it is part of some query key||value! 201 if questionIndex == 0 { 202 questionIndex = i 203 u.path = s[pathStartIndex:questionIndex] 204 } 205 case NumberSign: 206 if numberSignIndex == 0 { 207 numberSignIndex = i 208 if questionIndex == 0 { 209 u.path = s[pathStartIndex:numberSignIndex] 210 } else { 211 u.query = s[questionIndex+1 : numberSignIndex] // +1 due to we don't need '?' 212 } 213 } 214 case SP: 215 // Don't need to continue loop anymore 216 break Loop 217 } 218 } 219 220 uriEnd = i 221 if questionIndex == 0 && numberSignIndex == 0 { 222 u.path = s[pathStartIndex:uriEnd] 223 } 224 if numberSignIndex != 0 { 225 u.fragment = s[numberSignIndex+1 : uriEnd] // +1 due to we don't need '#' 226 } 227 if questionIndex != 0 && numberSignIndex == 0 { 228 u.query = s[questionIndex+1 : uriEnd] // +1 due to we don't need '?' 229 } 230 } 231 232 u.uri = s[:uriEnd] 233 u.uriAsByte = convert.UnsafeStringToByteSlice(s[:uriEnd]) 234 return 235 } 236 237 func (u *URI) len() (ln int) { 238 ln = 4 // 4 == len("://")+len("?") 239 ln += len(u.scheme) + len(u.authority) + len(u.path) + len(u.query) 240 return 241 }