feat: Inital commit

This commit is contained in:
2025-07-26 05:58:59 +00:00
commit 753d1c60ea
1849 changed files with 830533 additions and 0 deletions
+279
View File
@@ -0,0 +1,279 @@
/*
* Copyright 2020 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 "golang.org/x/text/encoding/charmap"
var defaultSpecialSchemes = map[string]string{
"ftp": "21",
"file": "",
"http": "80",
"https": "443",
"ws": "80",
"wss": "443",
}
// parserOptions configure a url parser. parserOptions are set by the ParserOption
// values passed to NewParser.
type parserOptions struct {
reportValidationErrors bool
failOnValidationError bool
laxHostParsing bool
collapseConsecutiveSlashes bool
acceptInvalidCodepoints bool
preParseHostFunc func(url *Url, host string) string
postParseHostFunc func(url *Url, host string) string
percentEncodeSinglePercentSign bool
allowSettingPathForNonBaseUrl bool
skipWindowsDriveLetterNormalization bool
specialSchemes map[string]string
skipTrailingSlashNormalization bool
encodingOverride *charmap.Charmap
pathPercentEncodeSet *PercentEncodeSet
specialQueryPercentEncodeSet *PercentEncodeSet
queryPercentEncodeSet *PercentEncodeSet
specialFragmentPercentEncodeSet *PercentEncodeSet
fragmentPercentEncodeSet *PercentEncodeSet
skipEqualsForEmptySearchParamsValue bool
}
// ParserOption configures how we parse a URL.
type ParserOption interface {
apply(*parserOptions)
}
// EmptyParserOption does not alter the parser configuration. It can be embedded in
// another structure to build custom parser options.
type EmptyParserOption struct{}
func (EmptyParserOption) apply(*parserOptions) {}
// funcParserOption wraps a function that modifies parserOptions into an
// implementation of the ParserOption interface.
type funcParserOption struct {
f func(*parserOptions)
}
func (fpo *funcParserOption) apply(po *parserOptions) {
fpo.f(po)
}
func newFuncParserOption(f func(*parserOptions)) *funcParserOption {
return &funcParserOption{
f: f,
}
}
func defaultParserOptions() parserOptions {
return parserOptions{
pathPercentEncodeSet: PathPercentEncodeSet,
specialQueryPercentEncodeSet: SpecialQueryPercentEncodeSet,
queryPercentEncodeSet: QueryPercentEncodeSet,
specialFragmentPercentEncodeSet: FragmentPercentEncodeSet,
fragmentPercentEncodeSet: FragmentPercentEncodeSet,
specialSchemes: defaultSpecialSchemes,
}
}
// WithReportValidationErrors records all non fatal validation errors so that they can be fetchd by a call to....
func WithReportValidationErrors() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.reportValidationErrors = true
})
}
// WithFailOnValidationError makes the parser throw an error on non fatal validation errors.
func WithFailOnValidationError() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.failOnValidationError = true
})
}
// WithLaxHostParsing ignores some decoding errors and returns the host as is.
//
// This API is EXPERIMENTAL.
func WithLaxHostParsing() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.laxHostParsing = true
})
}
// WithCollapseConsecutiveSlashes collapses consecutive slashes in path into one
// (e.g. http://example.com//foo///bar => http://example.com/foo/bar).
func WithCollapseConsecutiveSlashes() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.collapseConsecutiveSlashes = true
})
}
// WithAcceptInvalidCodepoints percent encodes values which are not valid UTF-8.
//
// This API is EXPERIMENTAL.
func WithAcceptInvalidCodepoints() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.acceptInvalidCodepoints = true
})
}
// WithPreParseHostFunc is a function which allows manipulation of host string before it is parsed.
//
// This API is EXPERIMENTAL.
func WithPreParseHostFunc(f func(url *Url, host string) string) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.preParseHostFunc = f
})
}
// WithPostParseHostFunc is a function which allows manipulation of host string after it is parsed.
// It is called only if the host isn't an IP address.
//
// This API is EXPERIMENTAL.
func WithPostParseHostFunc(f func(url *Url, host string) string) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.postParseHostFunc = f
})
}
// WithPercentEncodeSinglePercentSign percent encodes a '%' which is not followed by two hexadecimal digits
// instead of complaining about invalid percent encoding.
//
// This API is EXPERIMENTAL.
func WithPercentEncodeSinglePercentSign() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.percentEncodeSinglePercentSign = true
})
}
// WithAllowSettingPathForNonBaseUrl allows to set path for a url which cannot be a base url.
// WhathWg standard says this should be illegal
//
// This API is EXPERIMENTAL.
func WithAllowSettingPathForNonBaseUrl() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.allowSettingPathForNonBaseUrl = true
})
}
// WithSkipWindowsDriveLetterNormalization skips conversion of 'C|' to 'C:'.
// WhathWg standard says only a normalized Windows drive letter is conforming.
//
// This API is EXPERIMENTAL.
func WithSkipWindowsDriveLetterNormalization() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.skipWindowsDriveLetterNormalization = true
})
}
// WithSpecialSchemes allows overriding the notion of special schemes.
// special is a map of 'scheme' => 'default port'
//
// WhatWg standard removed gopher from special schemes. This is how you add it back:
//
// special := map[string]string{
// "ftp": "21",
// "file": "",
// "http": "80",
// "https": "443",
// "ws": "80",
// "wss": "443",
// "gopher": "70",
// }
//
// This API is EXPERIMENTAL.
func WithSpecialSchemes(special map[string]string) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.specialSchemes = special
})
}
// WithEncodingOverride allows to set an encoding other than UTF-8 when parsing.
//
// This API is EXPERIMENTAL.
func WithEncodingOverride(cm *charmap.Charmap) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.encodingOverride = cm
})
}
// WithPathPercentEncodeSet allows to set an alternative set of characters to percent encode in path component.
//
// This API is EXPERIMENTAL.
func WithPathPercentEncodeSet(encodeSet *PercentEncodeSet) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.pathPercentEncodeSet = encodeSet
})
}
// WithQueryPercentEncodeSet allows to set an alternative set of characters to percent encode in query component
// when scheme is not special.
//
// This API is EXPERIMENTAL.
func WithQueryPercentEncodeSet(encodeSet *PercentEncodeSet) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.queryPercentEncodeSet = encodeSet
})
}
// WithSpecialQueryPercentEncodeSet allows to set an alternative set of characters to percent encode in query component
// when scheme is special.
//
// This API is EXPERIMENTAL.
func WithSpecialQueryPercentEncodeSet(encodeSet *PercentEncodeSet) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.specialQueryPercentEncodeSet = encodeSet
})
}
// WithFragmentPathPercentEncodeSet allows to set an alternative set of characters to percent encode in fragment
// component when scheme is not special.
//
// This API is EXPERIMENTAL.
func WithFragmentPathPercentEncodeSet(encodeSet *PercentEncodeSet) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.fragmentPercentEncodeSet = encodeSet
})
}
// WithSpecialFragmentPathPercentEncodeSet allows to set an alternative set of characters to percent encode in fragment
// component when scheme is special.
//
// This API is EXPERIMENTAL.
func WithSpecialFragmentPathPercentEncodeSet(encodeSet *PercentEncodeSet) ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.fragmentPercentEncodeSet = encodeSet
})
}
// WithSkipTrailingSlashNormalization skips normalizing of empty paths.
//
// This API is EXPERIMENTAL.
func WithSkipTrailingSlashNormalization() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.skipTrailingSlashNormalization = true
})
}
// WithSkipEqualsForEmptySearchParamsValue skips writing '=' when setting an empty value for a search parameter.
//
// e.g. url.SearchParams().Set("name", "") gives 'http://...?name' instead of 'http://...?name='
//
// This API is EXPERIMENTAL.
func WithSkipEqualsForEmptySearchParamsValue() ParserOption {
return newFuncParserOption(func(o *parserOptions) {
o.skipEqualsForEmptySearchParamsValue = true
})
}