335 lines
7.7 KiB
Go
335 lines
7.7 KiB
Go
/*
|
|
* Copyright 2019 National Library of Norway.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package url
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
// Url represents a URL.
|
|
type Url struct {
|
|
inputUrl string
|
|
scheme string
|
|
username string
|
|
password string
|
|
host *string
|
|
port *string
|
|
decodedPort int
|
|
path *path
|
|
query *string
|
|
fragment *string
|
|
searchParams *SearchParams
|
|
validationErrors []error
|
|
parser *parser
|
|
isIPv4 bool
|
|
isIPv6 bool
|
|
}
|
|
|
|
// Href implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
// If excludeFragment is true, the fragment component will be excluded from the output.
|
|
func (u *Url) Href(excludeFragment bool) string {
|
|
output := u.scheme + ":"
|
|
if u.host != nil {
|
|
output += "//"
|
|
if u.username != "" || u.password != "" {
|
|
output += u.username
|
|
if u.password != "" {
|
|
output += ":" + u.password
|
|
}
|
|
output += "@"
|
|
}
|
|
output += *u.host
|
|
if u.port != nil {
|
|
output += ":" + *u.port
|
|
}
|
|
}
|
|
if u.host == nil && !u.path.isOpaque() && len(u.path.p) > 1 && u.path.p[0] == "" {
|
|
output += "/."
|
|
}
|
|
|
|
output += u.path.String()
|
|
|
|
if u.query != nil {
|
|
output += "?" + *u.query
|
|
}
|
|
|
|
if !excludeFragment && u.fragment != nil {
|
|
output += "#" + *u.fragment
|
|
}
|
|
|
|
return output
|
|
}
|
|
|
|
// Protocol implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Protocol() string {
|
|
return u.scheme + ":"
|
|
}
|
|
|
|
// SetProtocol implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetProtocol(scheme string) {
|
|
if !strings.HasSuffix(scheme, ":") {
|
|
scheme = scheme + ":"
|
|
}
|
|
_, _ = u.parser.BasicParser(scheme, nil, u, StateSchemeStart)
|
|
}
|
|
|
|
func (u *Url) Scheme() string {
|
|
return u.scheme
|
|
}
|
|
|
|
// Username implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Username() string {
|
|
return u.username
|
|
}
|
|
|
|
// SetUsername implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetUsername(username string) {
|
|
if u.host == nil || *u.host == "" || u.scheme == "file" {
|
|
return
|
|
}
|
|
u.username = u.parser.PercentEncodeString(username, UserInfoPercentEncodeSet)
|
|
}
|
|
|
|
// Password implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Password() string {
|
|
return u.password
|
|
}
|
|
|
|
// SetPassword implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetPassword(password string) {
|
|
if u.host == nil || *u.host == "" || u.scheme == "file" {
|
|
return
|
|
}
|
|
u.password = u.parser.PercentEncodeString(password, UserInfoPercentEncodeSet)
|
|
}
|
|
|
|
// Host implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Host() string {
|
|
if u.host == nil {
|
|
return ""
|
|
}
|
|
if u.port == nil {
|
|
return *u.host
|
|
}
|
|
return *u.host + ":" + *u.port
|
|
}
|
|
|
|
// SetHost implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetHost(host string) {
|
|
if u.path.isOpaque() {
|
|
return
|
|
}
|
|
_, _ = u.parser.BasicParser(host, nil, u, StateHost)
|
|
}
|
|
|
|
// Hostname implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Hostname() string {
|
|
if u.host == nil {
|
|
return ""
|
|
}
|
|
return *u.host
|
|
}
|
|
|
|
// SetHostname implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetHostname(host string) {
|
|
if u.path.isOpaque() {
|
|
return
|
|
}
|
|
_, _ = u.parser.BasicParser(host, nil, u, StateHostname)
|
|
}
|
|
|
|
// Port implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Port() string {
|
|
if u.port == nil {
|
|
return ""
|
|
}
|
|
return *u.port
|
|
}
|
|
|
|
// SetPort implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetPort(port string) {
|
|
if u.host == nil || *u.host == "" || u.scheme == "file" {
|
|
return
|
|
}
|
|
if port == "" {
|
|
u.port = nil
|
|
u.decodedPort = 0
|
|
} else {
|
|
_, _ = u.parser.BasicParser(port, nil, u, StatePort)
|
|
}
|
|
}
|
|
|
|
func (u *Url) DecodedPort() int {
|
|
if u.decodedPort == 0 {
|
|
return u.getDefaultPort()
|
|
} else {
|
|
return u.decodedPort
|
|
}
|
|
}
|
|
|
|
// Pathname implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Pathname() string {
|
|
return u.path.String()
|
|
}
|
|
|
|
// SetPathname implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetPathname(path string) {
|
|
if u.path.isOpaque() {
|
|
return
|
|
}
|
|
u.path.init()
|
|
_, _ = u.parser.BasicParser(path, nil, u, StatePathStart)
|
|
}
|
|
|
|
// OpaquePath tells if the path is opaque (https://url.spec.whatwg.org/#url-opaque-path)
|
|
func (u *Url) OpaquePath() bool {
|
|
return u.path.opaque
|
|
}
|
|
|
|
// Search implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Search() string {
|
|
if u.query == nil || len(*u.query) == 0 {
|
|
return ""
|
|
}
|
|
return "?" + *u.query
|
|
}
|
|
|
|
// SetSearch implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetSearch(query string) {
|
|
if query == "" {
|
|
u.query = nil
|
|
if u.searchParams != nil {
|
|
u.searchParams.params = u.searchParams.params[:0]
|
|
}
|
|
if u.fragment == nil && u.query == nil {
|
|
u.path.stripTrailingSpacesIfOpaque()
|
|
}
|
|
return
|
|
}
|
|
query = strings.TrimPrefix(query, "?")
|
|
if u.query == nil {
|
|
u.query = new(string)
|
|
}
|
|
_, _ = u.parser.BasicParser(query, nil, u, StateQuery)
|
|
if u.searchParams == nil {
|
|
u.newUrlSearchParams()
|
|
} else {
|
|
u.searchParams.init(*u.query)
|
|
}
|
|
}
|
|
|
|
// SearchParams implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SearchParams() *SearchParams {
|
|
if u.searchParams == nil {
|
|
u.newUrlSearchParams()
|
|
}
|
|
return u.searchParams
|
|
}
|
|
|
|
func (u *Url) SetSearchParams(searchParams *SearchParams) {
|
|
u.searchParams = searchParams
|
|
u.searchParams.update()
|
|
}
|
|
|
|
func (u *Url) Query() string {
|
|
if u.query == nil || len(*u.query) == 0 {
|
|
return ""
|
|
}
|
|
return *u.query
|
|
}
|
|
|
|
// Hash implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) Hash() string {
|
|
if u.fragment == nil || len(*u.fragment) == 0 {
|
|
return ""
|
|
}
|
|
return "#" + *u.fragment
|
|
}
|
|
|
|
// SetHash implements WHATWG url api (https://url.spec.whatwg.org/#api)
|
|
func (u *Url) SetHash(fragment string) {
|
|
if fragment == "" {
|
|
u.fragment = nil
|
|
if u.fragment == nil && u.query == nil {
|
|
u.path.stripTrailingSpacesIfOpaque()
|
|
}
|
|
return
|
|
}
|
|
fragment = strings.TrimPrefix(fragment, "#")
|
|
u.fragment = new(string)
|
|
_, _ = u.parser.BasicParser(fragment, nil, u, StateFragment)
|
|
}
|
|
|
|
func (u *Url) Fragment() string {
|
|
if u.fragment == nil || len(*u.fragment) == 0 {
|
|
return ""
|
|
}
|
|
return *u.fragment
|
|
}
|
|
|
|
func (u *Url) String() string {
|
|
return u.Href(false)
|
|
}
|
|
|
|
func (u *Url) ValidationErrors() []error {
|
|
return u.validationErrors
|
|
}
|
|
|
|
func (u *Url) newUrlSearchParams() {
|
|
usp := &SearchParams{url: u}
|
|
if u.query != nil {
|
|
usp.init(*u.query)
|
|
}
|
|
u.searchParams = usp
|
|
}
|
|
|
|
func (u *Url) IsIPv4() bool {
|
|
return u.isIPv4
|
|
}
|
|
|
|
func (u *Url) IsIPv6() bool {
|
|
return u.isIPv6
|
|
}
|
|
|
|
// Clone returns a deep copy of the URL.
|
|
func (u *Url) Clone() *Url {
|
|
return &Url{
|
|
inputUrl: u.inputUrl,
|
|
scheme: u.scheme,
|
|
username: u.username,
|
|
password: u.password,
|
|
host: cloneStringPointer(u.host),
|
|
port: cloneStringPointer(u.port),
|
|
decodedPort: u.decodedPort,
|
|
path: u.path.clone(),
|
|
query: cloneStringPointer(u.query),
|
|
fragment: cloneStringPointer(u.fragment),
|
|
searchParams: u.SearchParams().Clone(),
|
|
parser: u.parser,
|
|
isIPv4: u.isIPv4,
|
|
isIPv6: u.isIPv6,
|
|
}
|
|
}
|
|
|
|
func cloneStringPointer(s *string) *string {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
c := *s
|
|
return &c
|
|
}
|