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