github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/uri/uri.go (about)

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