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  }