package fasthttp

import (
	
	
	
	
	
	
	
)

// AcquireURI returns an empty URI instance from the pool.
//
// Release the URI with ReleaseURI after the URI is no longer needed.
// This allows reducing GC load.
func () *URI {
	return uriPool.Get().(*URI)
}

// ReleaseURI releases the URI acquired via AcquireURI.
//
// The released URI mustn't be used after releasing it, otherwise data races
// may occur.
func ( *URI) {
	.Reset()
	uriPool.Put()
}

var uriPool = &sync.Pool{
	New: func() interface{} {
		return &URI{}
	},
}

// URI represents URI :) .
//
// It is forbidden copying URI instances. Create new instance and use CopyTo
// instead.
//
// URI instance MUST NOT be used from concurrently running goroutines.
type URI struct {
	noCopy noCopy

	pathOriginal []byte
	scheme       []byte
	path         []byte
	queryString  []byte
	hash         []byte
	host         []byte

	queryArgs       Args
	parsedQueryArgs bool

	// Path values are sent as-is without normalization
	//
	// Disabled path normalization may be useful for proxying incoming requests
	// to servers that are expecting paths to be forwarded as-is.
	//
	// By default path values are normalized, i.e.
	// extra slashes are removed, special characters are encoded.
	DisablePathNormalizing bool

	fullURI    []byte
	requestURI []byte

	username []byte
	password []byte
}

// CopyTo copies uri contents to dst.
func ( *URI) ( *URI) {
	.Reset()
	.pathOriginal = append(.pathOriginal, .pathOriginal...)
	.scheme = append(.scheme, .scheme...)
	.path = append(.path, .path...)
	.queryString = append(.queryString, .queryString...)
	.hash = append(.hash, .hash...)
	.host = append(.host, .host...)
	.username = append(.username, .username...)
	.password = append(.password, .password...)

	.queryArgs.CopyTo(&.queryArgs)
	.parsedQueryArgs = .parsedQueryArgs
	.DisablePathNormalizing = .DisablePathNormalizing

	// fullURI and requestURI shouldn't be copied, since they are created
	// from scratch on each FullURI() and RequestURI() call.
}

// Hash returns URI hash, i.e. qwe of http://aaa.com/foo/bar?baz=123#qwe .
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	return .hash
}

// SetHash sets URI hash.
func ( *URI) ( string) {
	.hash = append(.hash[:0], ...)
}

// SetHashBytes sets URI hash.
func ( *URI) ( []byte) {
	.hash = append(.hash[:0], ...)
}

// Username returns URI username
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	return .username
}

// SetUsername sets URI username.
func ( *URI) ( string) {
	.username = append(.username[:0], ...)
}

// SetUsernameBytes sets URI username.
func ( *URI) ( []byte) {
	.username = append(.username[:0], ...)
}

// Password returns URI password
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	return .password
}

// SetPassword sets URI password.
func ( *URI) ( string) {
	.password = append(.password[:0], ...)
}

// SetPasswordBytes sets URI password.
func ( *URI) ( []byte) {
	.password = append(.password[:0], ...)
}

// QueryString returns URI query string,
// i.e. baz=123 of http://aaa.com/foo/bar?baz=123#qwe .
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	return .queryString
}

// SetQueryString sets URI query string.
func ( *URI) ( string) {
	.queryString = append(.queryString[:0], ...)
	.parsedQueryArgs = false
}

// SetQueryStringBytes sets URI query string.
func ( *URI) ( []byte) {
	.queryString = append(.queryString[:0], ...)
	.parsedQueryArgs = false
}

// Path returns URI path, i.e. /foo/bar of http://aaa.com/foo/bar?baz=123#qwe .
//
// The returned path is always urldecoded and normalized,
// i.e. '//f%20obar/baz/../zzz' becomes '/f obar/zzz'.
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	 := .path
	if len() == 0 {
		 = strSlash
	}
	return 
}

// SetPath sets URI path.
func ( *URI) ( string) {
	.pathOriginal = append(.pathOriginal[:0], ...)
	.path = normalizePath(.path, .pathOriginal)
}

// SetPathBytes sets URI path.
func ( *URI) ( []byte) {
	.pathOriginal = append(.pathOriginal[:0], ...)
	.path = normalizePath(.path, .pathOriginal)
}

// PathOriginal returns the original path from requestURI passed to URI.Parse().
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	return .pathOriginal
}

// Scheme returns URI scheme, i.e. http of http://aaa.com/foo/bar?baz=123#qwe .
//
// Returned scheme is always lowercased.
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	 := .scheme
	if len() == 0 {
		 = strHTTP
	}
	return 
}

// SetScheme sets URI scheme, i.e. http, https, ftp, etc.
func ( *URI) ( string) {
	.scheme = append(.scheme[:0], ...)
	lowercaseBytes(.scheme)
}

// SetSchemeBytes sets URI scheme, i.e. http, https, ftp, etc.
func ( *URI) ( []byte) {
	.scheme = append(.scheme[:0], ...)
	lowercaseBytes(.scheme)
}

func ( *URI) () bool {
	return bytes.Equal(.scheme, strHTTPS)
}

func ( *URI) () bool {
	return len(.scheme) == 0 || bytes.Equal(.scheme, strHTTP)
}

// Reset clears uri.
func ( *URI) () {
	.pathOriginal = .pathOriginal[:0]
	.scheme = .scheme[:0]
	.path = .path[:0]
	.queryString = .queryString[:0]
	.hash = .hash[:0]
	.username = .username[:0]
	.password = .password[:0]

	.host = .host[:0]
	.queryArgs.Reset()
	.parsedQueryArgs = false
	.DisablePathNormalizing = false

	// There is no need in u.fullURI = u.fullURI[:0], since full uri
	// is calculated on each call to FullURI().

	// There is no need in u.requestURI = u.requestURI[:0], since requestURI
	// is calculated on each call to RequestURI().
}

// Host returns host part, i.e. aaa.com of http://aaa.com/foo/bar?baz=123#qwe .
//
// Host is always lowercased.
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	return .host
}

// SetHost sets host for the uri.
func ( *URI) ( string) {
	.host = append(.host[:0], ...)
	lowercaseBytes(.host)
}

// SetHostBytes sets host for the uri.
func ( *URI) ( []byte) {
	.host = append(.host[:0], ...)
	lowercaseBytes(.host)
}

var ErrorInvalidURI = errors.New("invalid uri")

// Parse initializes URI from the given host and uri.
//
// host may be nil. In this case uri must contain fully qualified uri,
// i.e. with scheme and host. http is assumed if scheme is omitted.
//
// uri may contain e.g. RequestURI without scheme and host if host is non-empty.
func ( *URI) (,  []byte) error {
	return .parse(, , false)
}

func ( *URI) (,  []byte,  bool) error {
	.Reset()

	if stringContainsCTLByte() {
		return ErrorInvalidURI
	}

	if len() == 0 || bytes.Contains(, strColonSlashSlash) {
		, ,  := splitHostURI(, )
		.SetSchemeBytes()
		 = 
		 = 
	}

	if  {
		.SetSchemeBytes(strHTTPS)
	}

	if  := bytes.IndexByte(, '@');  >= 0 {
		 := [:]
		 = [+1:]

		if  := bytes.IndexByte(, ':');  >= 0 {
			.username = append(.username[:0], [:]...)
			.password = append(.password[:0], [+1:]...)
		} else {
			.username = append(.username[:0], ...)
			.password = .password[:0]
		}
	}

	.host = append(.host, ...)
	if ,  := parseHost(.host);  != nil {
		return 
	} else {
		.host = 
	}
	lowercaseBytes(.host)

	 := 
	 := bytes.IndexByte(, '?')
	 := bytes.IndexByte(, '#')
	// Ignore query in fragment part
	if  >= 0 &&  >  {
		 = -1
	}

	if  < 0 &&  < 0 {
		.pathOriginal = append(.pathOriginal, ...)
		.path = normalizePath(.path, .pathOriginal)
		return nil
	}

	if  >= 0 {
		// Path is everything up to the start of the query
		.pathOriginal = append(.pathOriginal, [:]...)
		.path = normalizePath(.path, .pathOriginal)

		if  < 0 {
			.queryString = append(.queryString, [+1:]...)
		} else {
			.queryString = append(.queryString, [+1:]...)
			.hash = append(.hash, [+1:]...)
		}
		return nil
	}

	// fragmentIndex >= 0 && queryIndex < 0
	// Path is up to the start of fragment
	.pathOriginal = append(.pathOriginal, [:]...)
	.path = normalizePath(.path, .pathOriginal)
	.hash = append(.hash, [+1:]...)

	return nil
}

// parseHost parses host as an authority without user
// information. That is, as host[:port].
//
// Based on https://github.com/golang/go/blob/8ac5cbe05d61df0a7a7c9a38ff33305d4dcfea32/src/net/url/url.go#L619
//
// The host is parsed and unescaped in place overwriting the contents of the host parameter.
func parseHost( []byte) ([]byte, error) {
	if len() > 0 && [0] == '[' {
		// Parse an IP-Literal in RFC 3986 and RFC 6874.
		// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
		 := bytes.LastIndexByte(, ']')
		if  < 0 {
			return nil, errors.New("missing ']' in host")
		}
		 := [+1:]
		if !validOptionalPort() {
			return nil, fmt.Errorf("invalid port %q after host", )
		}

		// RFC 6874 defines that %25 (%-encoded percent) introduces
		// the zone identifier, and the zone identifier can use basically
		// any %-encoding it likes. That's different from the host, which
		// can only %-encode non-ASCII bytes.
		// We do impose some restrictions on the zone, to avoid stupidity
		// like newlines.
		 := bytes.Index([:], []byte("%25"))
		if  >= 0 {
			,  := unescape([:], encodeHost)
			if  != nil {
				return nil, 
			}
			,  := unescape([:], encodeZone)
			if  != nil {
				return nil, 
			}
			,  := unescape([:], encodeHost)
			if  != nil {
				return nil, 
			}
			return append(, append(, ...)...), nil
		}
	} else if  := bytes.LastIndexByte(, ':');  != -1 {
		 := [:]
		if !validOptionalPort() {
			return nil, fmt.Errorf("invalid port %q after host", )
		}
	}

	var  error
	if ,  = unescape(, encodeHost);  != nil {
		return nil, 
	}
	return , nil
}

type encoding int

const (
	encodeHost encoding = 1 + iota
	encodeZone
)

type EscapeError string

func ( EscapeError) () string {
	return "invalid URL escape " + strconv.Quote(string())
}

type InvalidHostError string

func ( InvalidHostError) () string {
	return "invalid character " + strconv.Quote(string()) + " in host name"
}

// unescape unescapes a string; the mode specifies
// which section of the URL string is being unescaped.
//
// Based on https://github.com/golang/go/blob/8ac5cbe05d61df0a7a7c9a38ff33305d4dcfea32/src/net/url/url.go#L199
//
// Unescapes in place overwriting the contents of s and returning it.
func unescape( []byte,  encoding) ([]byte, error) {
	// Count %, check that they're well-formed.
	 := 0
	for  := 0;  < len(); {
		switch [] {
		case '%':
			++
			if +2 >= len() || !ishex([+1]) || !ishex([+2]) {
				 = [:]
				if len() > 3 {
					 = [:3]
				}
				return nil, EscapeError()
			}
			// Per https://tools.ietf.org/html/rfc3986#page-21
			// in the host component %-encoding can only be used
			// for non-ASCII bytes.
			// But https://tools.ietf.org/html/rfc6874#section-2
			// introduces %25 being allowed to escape a percent sign
			// in IPv6 scoped-address literals. Yay.
			if  == encodeHost && unhex([+1]) < 8 && !bytes.Equal([:+3], []byte("%25")) {
				return nil, EscapeError([ : +3])
			}
			if  == encodeZone {
				// RFC 6874 says basically "anything goes" for zone identifiers
				// and that even non-ASCII can be redundantly escaped,
				// but it seems prudent to restrict %-escaped bytes here to those
				// that are valid host name bytes in their unescaped form.
				// That is, you can use escaping in the zone identifier but not
				// to introduce bytes you couldn't just write directly.
				// But Windows puts spaces here! Yay.
				 := unhex([+1])<<4 | unhex([+2])
				if !bytes.Equal([:+3], []byte("%25")) &&  != ' ' && shouldEscape(, encodeHost) {
					return nil, EscapeError([ : +3])
				}
			}
			 += 3
		default:
			if ( == encodeHost ||  == encodeZone) && [] < 0x80 && shouldEscape([], ) {
				return nil, InvalidHostError([ : +1])
			}
			++
		}
	}

	if  == 0 {
		return , nil
	}

	 := [:0]
	for  := 0;  < len(); ++ {
		switch [] {
		case '%':
			 = append(, unhex([+1])<<4|unhex([+2]))
			 += 2
		default:
			 = append(, [])
		}
	}
	return , nil
}

// Return true if the specified character should be escaped when
// appearing in a URL string, according to RFC 3986.
//
// Please be informed that for now shouldEscape does not check all
// reserved characters correctly. See https://github.com/golang/go/issues/5684.
//
// Based on https://github.com/golang/go/blob/8ac5cbe05d61df0a7a7c9a38ff33305d4dcfea32/src/net/url/url.go#L100
func shouldEscape( byte,  encoding) bool {
	// §2.3 Unreserved characters (alphanum)
	if 'a' <=  &&  <= 'z' || 'A' <=  &&  <= 'Z' || '0' <=  &&  <= '9' {
		return false
	}

	if  == encodeHost ||  == encodeZone {
		// §3.2.2 Host allows
		//	sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
		// as part of reg-name.
		// We add : because we include :port as part of host.
		// We add [ ] because we include [ipv6]:port as part of host.
		// We add < > because they're the only characters left that
		// we could possibly allow, and Parse will reject them if we
		// escape them (because hosts can't use %-encoding for
		// ASCII bytes).
		switch  {
		case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']', '<', '>', '"':
			return false
		}
	}

	if  == '-' ||  == '_' ||  == '.' ||  == '~' { // §2.3 Unreserved characters (mark)
		return false
	}

	// Everything else must be escaped.
	return true
}

func ishex( byte) bool {
	return ('0' <=  &&  <= '9') ||
		('a' <=  &&  <= 'f') ||
		('A' <=  &&  <= 'F')
}

func unhex( byte) byte {
	switch {
	case '0' <=  &&  <= '9':
		return  - '0'
	case 'a' <=  &&  <= 'f':
		return  - 'a' + 10
	case 'A' <=  &&  <= 'F':
		return  - 'A' + 10
	}
	return 0
}

// validOptionalPort reports whether port is either an empty string
// or matches /^:\d*$/
func validOptionalPort( []byte) bool {
	if len() == 0 {
		return true
	}
	if [0] != ':' {
		return false
	}
	for ,  := range [1:] {
		if  < '0' ||  > '9' {
			return false
		}
	}
	return true
}

func normalizePath(,  []byte) []byte {
	 = [:0]
	 = addLeadingSlash(, )
	 = decodeArgAppendNoPlus(, )

	// remove duplicate slashes
	 := 
	 := len()
	for {
		 := bytes.Index(, strSlashSlash)
		if  < 0 {
			break
		}
		 = [:]
		copy(, [1:])
		 = [:len()-1]
		--
	}
	 = [:]

	// remove /./ parts
	 = 
	for {
		 := bytes.Index(, strSlashDotSlash)
		if  < 0 {
			break
		}
		 :=  + len(strSlashDotSlash) - 1
		copy([:], [:])
		 = [:len()-+]
	}

	// remove /foo/../ parts
	for {
		 := bytes.Index(, strSlashDotDotSlash)
		if  < 0 {
			break
		}
		 := bytes.LastIndexByte([:], '/')
		if  < 0 {
			 = 0
		}
		 += len(strSlashDotDotSlash) - 1
		copy([:], [:])
		 = [:len()-+]
	}

	// remove trailing /foo/..
	 := bytes.LastIndex(, strSlashDotDot)
	if  >= 0 && +len(strSlashDotDot) == len() {
		 := bytes.LastIndexByte([:], '/')
		if  < 0 {
			return append([:0], strSlash...)
		}
		 = [:+1]
	}

	if filepath.Separator == '\\' {
		// remove \.\ parts
		for {
			 := bytes.Index(, strBackSlashDotBackSlash)
			if  < 0 {
				break
			}
			 :=  + len(strSlashDotSlash) - 1
			copy([:], [:])
			 = [:len()-+]
		}

		// remove /foo/..\ parts
		for {
			 := bytes.Index(, strSlashDotDotBackSlash)
			if  < 0 {
				break
			}
			 := bytes.LastIndexByte([:], '/')
			if  < 0 {
				 = 0
			}
			++
			 += len(strSlashDotDotBackSlash)
			copy([:], [:])
			 = [:len()-+]
		}

		// remove /foo\..\ parts
		for {
			 := bytes.Index(, strBackSlashDotDotBackSlash)
			if  < 0 {
				break
			}
			 := bytes.LastIndexByte([:], '/')
			if  < 0 {
				 = 0
			}
			 += len(strBackSlashDotDotBackSlash) - 1
			copy([:], [:])
			 = [:len()-+]
		}

		// remove trailing \foo\..
		 := bytes.LastIndex(, strBackSlashDotDot)
		if  >= 0 && +len(strSlashDotDot) == len() {
			 := bytes.LastIndexByte([:], '/')
			if  < 0 {
				return append([:0], strSlash...)
			}
			 = [:+1]
		}
	}

	return 
}

// RequestURI returns RequestURI - i.e. URI without Scheme and Host.
func ( *URI) () []byte {
	var  []byte
	if .DisablePathNormalizing {
		 = .requestURI[:0]
		 = append(, .PathOriginal()...)
	} else {
		 = appendQuotedPath(.requestURI[:0], .Path())
	}
	if .parsedQueryArgs && .queryArgs.Len() > 0 {
		 = append(, '?')
		 = .queryArgs.AppendBytes()
	} else if len(.queryString) > 0 {
		 = append(, '?')
		 = append(, .queryString...)
	}
	.requestURI = 
	return .requestURI
}

// LastPathSegment returns the last part of uri path after '/'.
//
// Examples:
//
//   - For /foo/bar/baz.html path returns baz.html.
//   - For /foo/bar/ returns empty byte slice.
//   - For /foobar.js returns foobar.js.
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	 := .Path()
	 := bytes.LastIndexByte(, '/')
	if  < 0 {
		return 
	}
	return [+1:]
}

// Update updates uri.
//
// The following newURI types are accepted:
//
//   - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
//     uri is replaced by newURI.
//   - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
//     the original scheme is preserved.
//   - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
//     of the original uri is replaced.
//   - Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
//     is updated according to the new relative path.
func ( *URI) ( string) {
	.UpdateBytes(s2b())
}

// UpdateBytes updates uri.
//
// The following newURI types are accepted:
//
//   - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
//     uri is replaced by newURI.
//   - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
//     the original scheme is preserved.
//   - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
//     of the original uri is replaced.
//   - Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
//     is updated according to the new relative path.
func ( *URI) ( []byte) {
	.requestURI = .updateBytes(, .requestURI)
}

func ( *URI) (,  []byte) []byte {
	if len() == 0 {
		return 
	}

	 := bytes.Index(, strSlashSlash)
	if  >= 0 {
		// absolute uri
		var  [32]byte
		 := [:0]
		if len(.scheme) > 0 {
			 = append([]byte(nil), .scheme...)
		}
		if  := .Parse(nil, );  != nil {
			return nil
		}
		if len() > 0 && len(.scheme) == 0 {
			.scheme = append(.scheme[:0], ...)
		}
		return 
	}

	if [0] == '/' {
		// uri without host
		 = .appendSchemeHost([:0])
		 = append(, ...)
		if  := .Parse(nil, );  != nil {
			return nil
		}
		return 
	}

	// relative path
	switch [0] {
	case '?':
		// query string only update
		.SetQueryStringBytes([1:])
		return append([:0], .FullURI()...)
	case '#':
		// update only hash
		.SetHashBytes([1:])
		return append([:0], .FullURI()...)
	default:
		// update the last path part after the slash
		 := .Path()
		 = bytes.LastIndexByte(, '/')
		if  < 0 {
			panic(fmt.Sprintf("BUG: path must contain at least one slash: %q %q", .Path(), ))
		}
		 = .appendSchemeHost([:0])
		 = appendQuotedPath(, [:+1])
		 = append(, ...)
		if  := .Parse(nil, );  != nil {
			return nil
		}
		return 
	}
}

// FullURI returns full uri in the form {Scheme}://{Host}{RequestURI}#{Hash}.
//
// The returned bytes are valid until the next URI method call.
func ( *URI) () []byte {
	.fullURI = .AppendBytes(.fullURI[:0])
	return .fullURI
}

// AppendBytes appends full uri to dst and returns the extended dst.
func ( *URI) ( []byte) []byte {
	 = .appendSchemeHost()
	 = append(, .RequestURI()...)
	if len(.hash) > 0 {
		 = append(, '#')
		 = append(, .hash...)
	}
	return 
}

func ( *URI) ( []byte) []byte {
	 = append(, .Scheme()...)
	 = append(, strColonSlashSlash...)
	return append(, .Host()...)
}

// WriteTo writes full uri to w.
//
// WriteTo implements io.WriterTo interface.
func ( *URI) ( io.Writer) (int64, error) {
	,  := .Write(.FullURI())
	return int64(), 
}

// String returns full uri.
func ( *URI) () string {
	return string(.FullURI())
}

func splitHostURI(,  []byte) ([]byte, []byte, []byte) {
	 := bytes.Index(, strSlashSlash)
	if  < 0 {
		return strHTTP, , 
	}
	 := [:]
	if bytes.IndexByte(, '/') >= 0 {
		return strHTTP, , 
	}
	if len() > 0 && [len()-1] == ':' {
		 = [:len()-1]
	}
	 += len(strSlashSlash)
	 = [:]
	 = bytes.IndexByte(, '/')
	 := bytes.IndexByte(, '?')
	if  >= 0 &&  <  {
		// A hack for urls like foobar.com?a=b/xyz
		 = 
	} else if  < 0 {
		// A hack for bogus urls like foobar.com?a=b without
		// slash after host.
		if  >= 0 {
			return , [:], [:]
		}
		return , , strSlash
	}
	return , [:], [:]
}

// QueryArgs returns query args.
//
// The returned args are valid until the next URI method call.
func ( *URI) () *Args {
	.parseQueryArgs()
	return &.queryArgs
}

func ( *URI) () {
	if .parsedQueryArgs {
		return
	}
	.queryArgs.ParseBytes(.queryString)
	.parsedQueryArgs = true
}

// stringContainsCTLByte reports whether s contains any ASCII control character.
func stringContainsCTLByte( []byte) bool {
	for  := 0;  < len(); ++ {
		 := []
		if  < ' ' ||  == 0x7f {
			return true
		}
	}
	return false
}