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

     1  /* For license and copyright information please see LEGAL file in repository */
     2  
     3  package http
     4  
     5  import (
     6  	"io"
     7  
     8  	"../convert"
     9  	"../mediatype"
    10  	"../protocol"
    11  )
    12  
    13  // URI store http URI parts.
    14  // https://tools.ietf.org/html/rfc2616#section-3.2
    15  // https://tools.ietf.org/html/rfc2616#section-5.1.2
    16  // https://tools.ietf.org/html/rfc3986
    17  // Request-URI = "*" | absoluteURI | abs_path | authority
    18  // http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
    19  type URI struct {
    20  	uri       string
    21  	uriAsByte []byte
    22  	scheme    string // = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
    23  	authority string // host [ ":" port ]
    24  	host      string // host without port if any exist in authority
    25  	path      string //
    26  	query     string // encoded query values, without '?'
    27  	fragment  string // fragment for references, without '#'
    28  }
    29  
    30  func (u *URI) Init(uri string) { u.unmarshalFrom(uri) }
    31  func (u *URI) Reset() {
    32  	u.uri = ""
    33  	u.uriAsByte = []byte{}
    34  	u.scheme = ""
    35  	u.authority = ""
    36  	u.host = ""
    37  	u.path = ""
    38  	u.query = ""
    39  	u.fragment = ""
    40  }
    41  func (u *URI) Set(scheme, authority, path, query string) {
    42  	u.scheme, u.authority, u.path, u.query = scheme, authority, path, query
    43  }
    44  
    45  func (u *URI) URI() string       { return u.uri }
    46  func (u *URI) Scheme() string    { return u.scheme }
    47  func (u *URI) Authority() string { return u.authority }
    48  func (u *URI) Host() string      { return u.host }
    49  func (u *URI) Path() string      { return u.path }
    50  func (u *URI) Query() string     { return u.query }
    51  func (u *URI) Fragment() string  { return u.fragment }
    52  
    53  // checkHost check host of request by RFC 7230, section 5.3 rules: Must treat
    54  //		GET / HTTP/1.1
    55  //		Host: www.sabz.city
    56  // and
    57  //		GET https://www.sabz.city/ HTTP/1.1
    58  //		Host: apis.sabz.city
    59  // the same. In the second case, any Host line is ignored.
    60  func (u *URI) checkHost(h *header) {
    61  	if u.authority == "" {
    62  		u.host = h.Get(HeaderKeyHost)
    63  	}
    64  	// TODO::: decode host (remove port if exist) from authority
    65  	u.host = u.authority
    66  }
    67  
    68  /*
    69  ********** protocol.Codec interface **********
    70   */
    71  
    72  func (u *URI) MediaType() protocol.MediaType       { return mediatype.URI } // application/x-www-form-urlencoded
    73  func (u *URI) CompressType() protocol.CompressType { return nil }
    74  func (u *URI) Len() (ln int) {
    75  	ln = len(u.uriAsByte)
    76  	if ln == 0 {
    77  		ln = u.len()
    78  	}
    79  	return
    80  }
    81  
    82  func (u *URI) Decode(reader protocol.Reader) (err protocol.Error) {
    83  	// TODO:::
    84  	return
    85  }
    86  
    87  func (u *URI) Encode(writer protocol.Writer) (err protocol.Error) {
    88  	var encodedURI = u.Marshal()
    89  	var _, goErr = writer.Write(encodedURI)
    90  	if goErr != nil {
    91  		// err =
    92  	}
    93  	return
    94  }
    95  
    96  // Marshal encode URI data and return it.
    97  func (u *URI) Marshal() (encodedURI []byte) {
    98  	if u.uriAsByte == nil {
    99  		u.uriAsByte = make([]byte, 0, u.len())
   100  		u.marshalTo(u.uriAsByte)
   101  	}
   102  	return u.uriAsByte
   103  }
   104  
   105  // MarshalTo encode URI data to given httpPacket and update u.uri and return httpPacket with new len.
   106  func (u *URI) MarshalTo(httpPacket []byte) []byte {
   107  	if u.uriAsByte == nil {
   108  		return u.marshalTo(httpPacket)
   109  	}
   110  	return append(httpPacket, u.uriAsByte...)
   111  }
   112  
   113  // Unmarshal use to parse and decode given URI to u
   114  func (u *URI) Unmarshal(uri []byte) (err protocol.Error) {
   115  	u.uri = convert.UnsafeByteSliceToString(uri)
   116  	u.uriAsByte = uri
   117  	u.unmarshalFrom(u.uri)
   118  	return
   119  }
   120  
   121  // UnmarshalFrom use to parse and decode given URI to u
   122  func (u *URI) UnmarshalFrom(data []byte) (remaining []byte, err protocol.Error) {
   123  	var uriEnd = u.unmarshalFrom(convert.UnsafeByteSliceToString(data))
   124  	remaining = data[uriEnd:]
   125  	return
   126  }
   127  
   128  /*
   129  ********** io package interfaces **********
   130   */
   131  
   132  func (u *URI) WriteTo(writer io.Writer) (n int64, err error) {
   133  	var encodedURI = u.Marshal()
   134  	var writeLength int
   135  	writeLength, err = writer.Write(encodedURI)
   136  	n = int64(writeLength)
   137  	return
   138  }
   139  
   140  /*
   141  ********** local methods **********
   142   */
   143  
   144  func (u *URI) marshalTo(httpPacket []byte) []byte {
   145  	var uriStart = len(httpPacket)
   146  	if u.scheme != "" {
   147  		httpPacket = append(httpPacket, u.scheme...)
   148  		httpPacket = append(httpPacket, "://"...)
   149  	}
   150  	httpPacket = append(httpPacket, u.authority...)
   151  	if u.path == "" {
   152  		httpPacket = append(httpPacket, Slash)
   153  	} else {
   154  		httpPacket = append(httpPacket, u.path...)
   155  	}
   156  	if u.query != "" {
   157  		httpPacket = append(httpPacket, Question)
   158  		httpPacket = append(httpPacket, u.query...)
   159  	}
   160  
   161  	// TODO::: below code cause memory leak if dev use u.uriAsByte||u.uri in other places due to GC can't free whole http packet
   162  	u.uriAsByte = httpPacket[uriStart:]
   163  	u.uri = convert.UnsafeByteSliceToString(u.uriAsByte)
   164  	return httpPacket
   165  }
   166  
   167  // unmarshalFrom use to parse and decode given URI to u
   168  func (u *URI) unmarshalFrom(s string) (uriEnd int) {
   169  	if s[0] == Asterisk {
   170  		uriEnd = 1
   171  	} else {
   172  		var originForm bool
   173  		if s[0] == '/' {
   174  			originForm = true
   175  		}
   176  
   177  		var authorityStartIndex, pathStartIndex, questionIndex, numberSignIndex int
   178  		var ln = len(s)
   179  		var i int
   180  	Loop:
   181  		for i = 0; i < ln; i++ {
   182  			switch s[i] {
   183  			case Colon:
   184  				// Check : mark is first appear before any start||end sign or it is part of others!
   185  				if authorityStartIndex == 0 {
   186  					u.scheme = s[:i]
   187  					i += 2                      // next loop will i+=1 so we just add i+=2
   188  					authorityStartIndex = i + 1 // +3 due to have ://
   189  				}
   190  			case Slash:
   191  				// Just check slash in middle of URI! If URI in origin form pathStartIndex always be 0!
   192  				if authorityStartIndex != 0 && pathStartIndex == 0 {
   193  					pathStartIndex = i
   194  					u.authority = s[authorityStartIndex:pathStartIndex]
   195  				} else if !originForm && pathStartIndex == 0 && i != 0 {
   196  					pathStartIndex = i
   197  					u.authority = s[:i]
   198  				}
   199  			case Question:
   200  				// Check ? mark is first appear or it is part of some query key||value!
   201  				if questionIndex == 0 {
   202  					questionIndex = i
   203  					u.path = s[pathStartIndex:questionIndex]
   204  				}
   205  			case NumberSign:
   206  				if numberSignIndex == 0 {
   207  					numberSignIndex = i
   208  					if questionIndex == 0 {
   209  						u.path = s[pathStartIndex:numberSignIndex]
   210  					} else {
   211  						u.query = s[questionIndex+1 : numberSignIndex] // +1 due to we don't need '?'
   212  					}
   213  				}
   214  			case SP:
   215  				// Don't need to continue loop anymore
   216  				break Loop
   217  			}
   218  		}
   219  
   220  		uriEnd = i
   221  		if questionIndex == 0 && numberSignIndex == 0 {
   222  			u.path = s[pathStartIndex:uriEnd]
   223  		}
   224  		if numberSignIndex != 0 {
   225  			u.fragment = s[numberSignIndex+1 : uriEnd] // +1 due to we don't need '#'
   226  		}
   227  		if questionIndex != 0 && numberSignIndex == 0 {
   228  			u.query = s[questionIndex+1 : uriEnd] // +1 due to we don't need '?'
   229  		}
   230  	}
   231  
   232  	u.uri = s[:uriEnd]
   233  	u.uriAsByte = convert.UnsafeStringToByteSlice(s[:uriEnd])
   234  	return
   235  }
   236  
   237  func (u *URI) len() (ln int) {
   238  	ln = 4 // 4 == len("://")+len("?")
   239  	ln += len(u.scheme) + len(u.authority) + len(u.path) + len(u.query)
   240  	return
   241  }