github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/http/body.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 8 "github.com/GeniusesGroup/libgo/compress/raw" 9 "github.com/GeniusesGroup/libgo/protocol" 10 ) 11 12 // body is represent HTTP body. 13 // Due to many performance impact, MediaType() method of body not return any true data. use header ContentType() method instead. This can be change if ... 14 // https://datatracker.ietf.org/doc/html/rfc2616#section-4.3 15 type body struct { 16 protocol.Codec 17 } 18 19 func (b *body) Init() {} 20 func (b *body) Reinit() { b.Codec = nil } 21 func (b *body) Deinit() {} 22 23 func (b *body) Body() protocol.Codec { return b } 24 func (b *body) SetBody(codec protocol.Codec) { b.Codec = codec } 25 26 //libgo:impl protocol.Codec 27 func (b *body) Len() int { 28 if b.Codec != nil { 29 return b.Codec.Len() 30 } 31 return 0 32 } 33 func (b *body) MediaType() protocol.MediaType { 34 if b.Codec != nil { 35 return b.Codec.MediaType() 36 } 37 return nil 38 } 39 func (b *body) CompressType() protocol.CompressType { 40 if b.Codec != nil { 41 return b.Codec.CompressType() 42 } 43 return nil 44 } 45 func (b *body) Decode(source protocol.Codec) (n int, err protocol.Error) { 46 if b.Codec != nil { 47 n, err = b.Codec.Decode(source) 48 } 49 return 50 } 51 func (b *body) Encode(destination protocol.Codec) (n int, err protocol.Error) { 52 if b.Codec != nil { 53 n, err = b.Codec.Encode(destination) 54 } 55 return 56 } 57 func (b *body) Marshal() (data []byte, err protocol.Error) { 58 if b.Codec != nil { 59 data, err = b.Codec.Marshal() 60 } 61 return 62 } 63 func (b *body) MarshalTo(data []byte) (added []byte, err protocol.Error) { 64 if b.Codec != nil { 65 return b.Codec.MarshalTo(data) 66 } 67 return data, nil 68 } 69 func (b *body) Unmarshal(data []byte) (n int, err protocol.Error) { 70 if b.Codec != nil { 71 n, err = b.Codec.Unmarshal(data) 72 } 73 return 74 } 75 func (b *body) UnmarshalFrom(data []byte) (remaining []byte, err protocol.Error) { 76 if b.Codec != nil { 77 return b.Codec.UnmarshalFrom(data) 78 } 79 return 80 } 81 82 // ReadFrom decodes r *Request data by read from given io.Reader 83 // 84 //libgo:impl io.ReaderFrom 85 func (b *body) ReadFrom(reader io.Reader) (n int64, goErr error) { 86 87 return 88 } 89 90 // WriteTo encodes r(*Request) data and write it to given io.Writer 91 // 92 //libgo:impl io.WriterTo 93 func (b *body) WriteTo(writer io.Writer) (n int64, err error) { 94 95 return 96 } 97 98 /* 99 ********** local methods ********** 100 */ 101 102 func (b *body) checkAndSetCodecAsIncomeBody(maybeBody []byte, c protocol.Codec, h *header) (err protocol.Error) { 103 var transferEncoding, _ = h.TransferEncoding() 104 switch transferEncoding { 105 case "": 106 var contentLength = h.ContentLength() 107 var maybeBodyLength = len(maybeBody) 108 if maybeBodyLength == int(contentLength) { 109 b.setReadedIncomeBody(maybeBody, h) 110 } else { 111 // TODO::: allow this situation that peer send some part of body with header?? 112 // Header length maybe other than stream income data length e.g. send body in multiple TCP.PSH flag set. 113 if maybeBodyLength > 0 { 114 var bodySlice = make([]byte, maybeBodyLength, contentLength) 115 copy(bodySlice, maybeBody) 116 for { 117 bodySlice, err = c.MarshalTo(bodySlice) 118 if err != nil || len(bodySlice) == int(contentLength) { 119 break 120 } 121 } 122 b.setReadedIncomeBody(bodySlice, h) 123 } else { 124 b.setCodecAsIncomeBody(c, h) 125 } 126 } 127 case HeaderValueChunked: 128 // TODO::: 129 default: 130 // Like nginx, due to security, we only support a single Transfer-Encoding header field, and 131 // only if set to "chunked". 132 // err = 133 } 134 return 135 } 136 137 func (b *body) checkAndSetReaderAsIncomeBody(maybeBody []byte, reader protocol.Reader, h *header) (err protocol.Error) { 138 var transferEncoding, _ = h.TransferEncoding() 139 switch transferEncoding { 140 case "": 141 var contentLength = h.ContentLength() 142 var maybeBodyLength = len(maybeBody) 143 if maybeBodyLength == int(contentLength) { 144 b.setReadedIncomeBody(maybeBody, h) 145 } else { 146 // Header length maybe other than stream income data length e.g. send body in multiple TCP.PSH flag set. 147 if maybeBodyLength > 0 { 148 var bodyReadLength int 149 var goErr error 150 var bodySlice = make([]byte, contentLength) 151 bodyReadLength, goErr = reader.Read(bodySlice[maybeBodyLength:]) 152 if goErr != nil { 153 // err = 154 return 155 } 156 if bodyReadLength+maybeBodyLength != int(contentLength) { 157 // err = 158 return 159 } 160 copy(bodySlice, maybeBody) 161 b.setReadedIncomeBody(bodySlice, h) 162 } else { 163 b.setReaderAsIncomeBody(reader, h, contentLength) 164 } 165 } 166 case HeaderValueChunked: 167 // TODO::: 168 default: 169 // Like nginx, due to security, we only support a single Transfer-Encoding header field, and 170 // only if set to "chunked". 171 // err = 172 } 173 return 174 } 175 176 // Call this method just if body marshaled with first line and headers. 177 func (b *body) checkAndSetIncomeBody(maybeBody []byte, h *header) (err protocol.Error) { 178 var maybeBodyLength = len(maybeBody) 179 if maybeBodyLength > 0 { 180 var contentLength = h.ContentLength() 181 if maybeBodyLength == int(contentLength) { 182 b.setReadedIncomeBody(maybeBody, h) 183 } else { 184 // err = 185 } 186 } 187 return 188 } 189 190 func (b *body) setCodecAsIncomeBody(c protocol.Codec, h *header) (err protocol.Error) { 191 var contentEncoding, _ = h.ContentEncoding() 192 if contentEncoding == "" { 193 b.Codec = c 194 return 195 } 196 197 var compressType protocol.CompressType 198 compressType, err = protocol.OS.GetCompressTypeByContentEncoding(contentEncoding) 199 if err != nil { 200 return 201 } 202 b.Codec, err = compressType.Decompress(c) 203 return 204 } 205 206 func (b *body) setReaderAsIncomeBody(reader protocol.Reader, h *header, contentLength uint64) (err protocol.Error) { 207 var contentEncoding, _ = h.ContentEncoding() 208 if contentEncoding == "" { 209 b.Codec, err = raw.RAW.DecompressFromReader(reader, int(contentLength)) 210 return 211 } 212 213 var compressType protocol.CompressType 214 compressType, err = protocol.OS.GetCompressTypeByContentEncoding(contentEncoding) 215 if err != nil { 216 return 217 } 218 b.Codec, err = compressType.DecompressFromReader(reader, int(contentLength)) 219 return 220 } 221 222 func (b *body) setReadedIncomeBody(body []byte, h *header) (err protocol.Error) { 223 var contentEncoding, _ = h.ContentEncoding() 224 if contentEncoding == "" { 225 b.Codec, err = raw.RAW.DecompressFromSlice(body) 226 return 227 } 228 229 var compressType protocol.CompressType 230 compressType, err = protocol.OS.GetCompressTypeByContentEncoding(contentEncoding) 231 if err != nil { 232 return 233 } 234 b.Codec, err = compressType.DecompressFromSlice(body) 235 return 236 }