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