github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/http/request.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 "io" 7 "strings" 8 9 "github.com/GeniusesGroup/libgo/codec" 10 "github.com/GeniusesGroup/libgo/convert" 11 "github.com/GeniusesGroup/libgo/protocol" 12 "github.com/GeniusesGroup/libgo/uri" 13 ) 14 15 // Request is represent HTTP request protocol structure. 16 // https://tools.ietf.org/html/rfc2616#section-5 17 type Request struct { 18 method string 19 uri uri.URI 20 version string 21 22 H header // Exported field to let consumers use other methods that protocol.HTTPHeader 23 body 24 } 25 26 func (r *Request) Init() { 27 r.H.Init() 28 r.body.Init() 29 } 30 func (r *Request) Reinit() { 31 r.method = "" 32 r.uri.Reinit() 33 r.version = "" 34 r.H.Reinit() 35 r.body.Reinit() 36 } 37 func (r *Request) Deinit() { 38 r.H.Deinit() 39 r.body.Deinit() 40 } 41 42 func (r *Request) Method() string { return r.method } 43 func (r *Request) URI() protocol.URI { return &r.uri } 44 func (r *Request) Version() string { return r.version } 45 func (r *Request) SetMethod(method string) { r.method = method } 46 func (r *Request) SetVersion(version string) { r.version = version } 47 func (r *Request) Header() protocol.HTTPHeader { return &r.H } 48 49 //libgo:impl protocol.Codec 50 func (r *Request) MediaType() protocol.MediaType { return &MediaTypeRequest } 51 func (r *Request) CompressType() protocol.CompressType { return nil } 52 func (r *Request) Len() (ln int) { 53 ln = r.LenWithoutBody() 54 ln += r.body.Len() 55 return 56 } 57 func (r *Request) Decode(source protocol.Codec) (n int, err protocol.Error) { 58 if source.Len() > MaxHTTPHeaderSize { 59 // err = 60 return 61 } 62 63 // Make a buffer to hold incoming data. 64 // TODO::: change to get from buffer pool?? 65 var data = make([]byte, 0, MaxHTTPHeaderSize) 66 data, err = source.MarshalTo(data) 67 if err != nil { 68 return 69 } 70 71 data, err = r.UnmarshalFrom(data) 72 if err != nil { 73 return 74 } 75 err = r.body.checkAndSetCodecAsIncomeBody(data, source, &r.H) 76 return 77 } 78 func (r *Request) Encode(destination protocol.Codec) (n int, err protocol.Error) { 79 var lenWithoutBody = r.LenWithoutBody() 80 var bodyLen = r.body.Len() 81 var wholeLen = lenWithoutBody + bodyLen 82 // Check if whole request has fewer length than MaxHTTPHeaderSize and Decide to send header and body separately 83 if wholeLen > MaxHTTPHeaderSize { 84 var withoutBody = make([]byte, 0, lenWithoutBody) 85 withoutBody = r.MarshalToWithoutBody(withoutBody) 86 87 n, err = destination.Unmarshal(withoutBody) 88 if err == nil && r.body.Codec != nil { 89 var bodyWrote int 90 bodyWrote, err = destination.Encode(&r.body) 91 n += bodyWrote 92 } 93 } else { 94 var httpPacket = make([]byte, 0, wholeLen) 95 httpPacket, err = r.MarshalTo(httpPacket) 96 n, err = destination.Unmarshal(httpPacket) 97 } 98 return 99 } 100 101 // Marshal encodes whole r *Request data and return httpPacket. 102 func (r *Request) Marshal() (httpPacket []byte, err protocol.Error) { 103 httpPacket = make([]byte, 0, r.Len()) 104 httpPacket, err = r.MarshalTo(httpPacket) 105 return 106 } 107 108 // MarshalTo encodes whole r *Request data to given httpPacket and return it with new len. 109 func (r *Request) MarshalTo(httpPacket []byte) (added []byte, err protocol.Error) { 110 httpPacket = append(httpPacket, r.method...) 111 httpPacket = append(httpPacket, SP) 112 httpPacket = r.uri.MarshalTo(httpPacket) 113 httpPacket = append(httpPacket, SP) 114 httpPacket = append(httpPacket, r.version...) 115 httpPacket = append(httpPacket, CRLF...) 116 117 httpPacket = r.H.MarshalTo(httpPacket) 118 httpPacket = append(httpPacket, CRLF...) 119 120 httpPacket, err = r.body.MarshalTo(httpPacket) 121 added = httpPacket 122 return 123 } 124 125 // Unmarshal parses and decodes data of given httpPacket to r *Request. 126 // In some bad packet may occur panic, handle panic by recover otherwise app will crash and exit! 127 func (r *Request) Unmarshal(httpPacket []byte) (n int, err protocol.Error) { 128 var maybeBody []byte 129 maybeBody, err = r.UnmarshalFrom(httpPacket) 130 if err != nil { 131 return 132 } 133 err = r.body.checkAndSetIncomeBody(maybeBody, &r.H) 134 n = len(httpPacket) 135 return 136 } 137 138 // Unmarshal parses and decodes data of given httpPacket to r *Request. 139 // In some bad packet may occur panic, handle panic by recover otherwise app will crash and exit! 140 func (r *Request) UnmarshalFrom(httpPacket []byte) (maybeBody []byte, err protocol.Error) { 141 // By use unsafe pointer here all strings assign in Request will just point to httpPacket slice 142 // and no need to alloc lot of new memory locations and copy request line and headers keys & values! 143 var s = convert.UnsafeByteSliceToString(httpPacket) 144 145 // si hold s index and i hold s index in new sliced state. 146 var si, i int 147 148 // First line: GET /index.html HTTP/1.0 149 i = strings.IndexByte(s[:methodMaxLength], SP) 150 if i == -1 { 151 maybeBody = httpPacket[si:] 152 err = &ErrParseMethod 153 return 154 } 155 r.method = s[:i] 156 i++ // +1 due to have ' ' 157 si = i 158 s = s[i:] 159 160 i = r.uri.UnmarshalFromString(s) 161 i++ // +1 due to have ' ' 162 si += i 163 s = s[i:] 164 165 i = strings.IndexByte(s[:versionMaxLength], '\r') 166 if i == -1 { 167 maybeBody = httpPacket[si:] 168 err = &ErrParseVersion 169 return 170 } 171 r.version = s[:i] 172 i += 2 // +2 due to have "\r\n" 173 si += i 174 s = s[i:] 175 176 i, err = r.H.unmarshal(s) 177 if err != nil { 178 maybeBody = httpPacket[i:] 179 return 180 } 181 si += i 182 // By https://tools.ietf.org/html/rfc2616#section-4 very simple http packet must end with CRLF even packet without header or body. 183 // So it can be occur panic if very simple request end without any CRLF 184 si += 2 // +2 due to have "\r\n" after header end 185 186 r.checkHost() 187 188 return httpPacket[si:], nil 189 } 190 191 /* 192 ********** protocol.Buffer interface ********** 193 */ 194 195 // ReadFrom decodes r *Request data by read from given io.Reader 196 // Declare to respect io.ReaderFrom interface. 197 func (r *Request) ReadFrom(reader io.Reader) (n int64, goErr error) { 198 // Make a buffer to hold incoming data. 199 var buf = make([]byte, MaxHTTPHeaderSize) 200 var headerReadLength int 201 var err protocol.Error 202 203 // Read the incoming connection into the buffer. 204 headerReadLength, goErr = reader.Read(buf) 205 if goErr != nil || headerReadLength == 0 { 206 return 207 } 208 209 buf = buf[:headerReadLength] 210 buf, err = r.UnmarshalFrom(buf) 211 if err != nil { 212 return int64(headerReadLength), err 213 } 214 err = r.body.checkAndSetReaderAsIncomeBody(buf, codec.ReaderAdaptor{reader}, &r.H) 215 n = int64(headerReadLength) 216 return 217 } 218 219 // WriteTo encodes r(*Request) data and write it to given io.Writer 220 // Declare to respect io.WriterTo interface. 221 func (r *Request) WriteTo(writer io.Writer) (n int64, err error) { 222 var lenWithoutBody = r.LenWithoutBody() 223 var bodyLen = r.body.Len() 224 var wholeLen = lenWithoutBody + bodyLen 225 // Check if whole request has fewer length than MaxHTTPHeaderSize and Decide to send header and body separately 226 if wholeLen > MaxHTTPHeaderSize { 227 var httpPacket = make([]byte, 0, lenWithoutBody) 228 httpPacket = r.MarshalToWithoutBody(httpPacket) 229 230 var headerWriteLength int 231 headerWriteLength, err = writer.Write(httpPacket) 232 if err == nil && r.body.Codec != nil { 233 n, err = r.body.WriteTo(writer) 234 } 235 n += int64(headerWriteLength) 236 } else { 237 var httpPacket = make([]byte, 0, wholeLen) 238 httpPacket, _ = r.MarshalTo(httpPacket) 239 var packetWriteLength int 240 packetWriteLength, err = writer.Write(httpPacket) 241 n = int64(packetWriteLength) 242 } 243 return 244 } 245 246 /* 247 ********** Other methods ********** 248 */ 249 250 // MarshalWithoutBody encodes r *Request data and return httpPacket without body part! 251 func (r *Request) MarshalWithoutBody() (httpPacket []byte) { 252 httpPacket = make([]byte, 0, r.LenWithoutBody()) 253 httpPacket = r.MarshalToWithoutBody(httpPacket) 254 return 255 } 256 257 // MarshalWithoutBody encodes r *Request data and return httpPacket without body part! 258 func (r *Request) MarshalToWithoutBody(httpPacket []byte) []byte { 259 httpPacket = append(httpPacket, r.method...) 260 httpPacket = append(httpPacket, SP) 261 httpPacket = r.uri.MarshalTo(httpPacket) 262 httpPacket = append(httpPacket, SP) 263 httpPacket = append(httpPacket, r.version...) 264 httpPacket = append(httpPacket, CRLF...) 265 266 httpPacket = r.H.MarshalTo(httpPacket) 267 httpPacket = append(httpPacket, CRLF...) 268 return httpPacket 269 } 270 271 // LenWithoutBody return length of request without body length 272 func (r *Request) LenWithoutBody() (ln int) { 273 ln = 6 // 6=1+1+2+2=len(SP)+len(SP)+len(CRLF)+len(CRLF) 274 ln += len(r.method) 275 ln += r.uri.Len() 276 ln += len(r.version) 277 ln += r.H.Len() 278 return 279 } 280 281 /* 282 ********** local Request methods ********** 283 */ 284 285 // checkHost check host of request by RFC 7230, section 5.3 rules: Must treat 286 // 287 // GET / HTTP/1.1 288 // Host: www.sabz.city 289 // 290 // and 291 // 292 // GET https://www.sabz.city/ HTTP/1.1 293 // Host: apis.sabz.city 294 // 295 // the same. In the second case, any Host line is ignored. 296 func (r *Request) checkHost() { 297 if r.uri.Authority() == "" { 298 r.uri.SetAuthority(r.H.Get(HeaderKeyHost)) 299 } 300 }