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