github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/http/body.go (about)

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