mirror of
https://github.com/minio/minio.git
synced 2025-11-09 21:49:46 -05:00
make notification as separate package (#5294)
* Remove old notification files * Add net package * Add event package * Modify minio to take new notification system
This commit is contained in:
150
pkg/net/host.go
Normal file
150
pkg/net/host.go
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* 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 net
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var hostLabelRegexp = regexp.MustCompile("^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$")
|
||||
|
||||
// Host - holds network host IP/name and its port.
|
||||
type Host struct {
|
||||
Name string
|
||||
Port Port
|
||||
IsPortSet bool
|
||||
}
|
||||
|
||||
// IsEmpty - returns whether Host is empty or not
|
||||
func (host Host) IsEmpty() bool {
|
||||
return host.Name == ""
|
||||
}
|
||||
|
||||
// String - returns string representation of Host.
|
||||
func (host Host) String() string {
|
||||
if !host.IsPortSet {
|
||||
return host.Name
|
||||
}
|
||||
|
||||
return host.Name + ":" + host.Port.String()
|
||||
}
|
||||
|
||||
// Equal - checks whether given host is equal or not.
|
||||
func (host Host) Equal(compHost Host) bool {
|
||||
return host.String() == compHost.String()
|
||||
}
|
||||
|
||||
// MarshalJSON - converts Host into JSON data
|
||||
func (host Host) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(host.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON - parses data into Host.
|
||||
func (host *Host) UnmarshalJSON(data []byte) (err error) {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Allow empty string
|
||||
if s == "" {
|
||||
*host = Host{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var h *Host
|
||||
if h, err = ParseHost(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*host = *h
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseHost - parses string into Host
|
||||
func ParseHost(s string) (*Host, error) {
|
||||
isValidHost := func(host string) bool {
|
||||
if host == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// host is not a valid IPv4 or IPv6 address
|
||||
// host may be a hostname
|
||||
// refer https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
|
||||
// why checks are done like below
|
||||
if len(host) < 1 || len(host) > 253 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, label := range strings.Split(host, ".") {
|
||||
if len(label) < 1 || len(label) > 63 {
|
||||
return false
|
||||
}
|
||||
|
||||
if !hostLabelRegexp.MatchString(label) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var port Port
|
||||
var isPortSet bool
|
||||
host, portStr, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "missing port in address") {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host = s
|
||||
portStr = ""
|
||||
} else {
|
||||
if port, err = ParsePort(portStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isPortSet = true
|
||||
}
|
||||
|
||||
if !isValidHost(host) {
|
||||
return nil, errors.New("invalid hostname")
|
||||
}
|
||||
|
||||
return &Host{
|
||||
Name: host,
|
||||
Port: port,
|
||||
IsPortSet: isPortSet,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustParseHost - parses given string to Host, else panics.
|
||||
func MustParseHost(s string) *Host {
|
||||
host, err := ParseHost(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return host
|
||||
}
|
||||
236
pkg/net/host_test.go
Normal file
236
pkg/net/host_test.go
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* 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 net
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHostIsEmpty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
host Host
|
||||
expectedResult bool
|
||||
}{
|
||||
{Host{"", 0, false}, true},
|
||||
{Host{"", 0, true}, true},
|
||||
{Host{"play", 9000, false}, false},
|
||||
{Host{"play", 9000, true}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.host.IsEmpty()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("test %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
host Host
|
||||
expectedStr string
|
||||
}{
|
||||
{Host{"", 0, false}, ""},
|
||||
{Host{"", 0, true}, ":0"},
|
||||
{Host{"play", 9000, false}, "play"},
|
||||
{Host{"play", 9000, true}, "play:9000"},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
str := testCase.host.String()
|
||||
|
||||
if str != testCase.expectedStr {
|
||||
t.Fatalf("test %v: string: expected: %v, got: %v", i+1, testCase.expectedStr, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostEqual(t *testing.T) {
|
||||
testCases := []struct {
|
||||
host Host
|
||||
compHost Host
|
||||
expectedResult bool
|
||||
}{
|
||||
{Host{"", 0, false}, Host{"", 0, true}, false},
|
||||
{Host{"play", 9000, true}, Host{"play", 9000, false}, false},
|
||||
{Host{"", 0, true}, Host{"", 0, true}, true},
|
||||
{Host{"play", 9000, false}, Host{"play", 9000, false}, true},
|
||||
{Host{"play", 9000, true}, Host{"play", 9000, true}, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.host.Equal(testCase.compHost)
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("test %v: string: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
host Host
|
||||
expectedData []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{Host{}, []byte(`""`), false},
|
||||
{Host{"play", 0, false}, []byte(`"play"`), false},
|
||||
{Host{"play", 0, true}, []byte(`"play:0"`), false},
|
||||
{Host{"play", 9000, true}, []byte(`"play:9000"`), false},
|
||||
{Host{"play.minio.io", 0, false}, []byte(`"play.minio.io"`), false},
|
||||
{Host{"play.minio.io", 9000, true}, []byte(`"play.minio.io:9000"`), false},
|
||||
{Host{"147.75.201.93", 0, false}, []byte(`"147.75.201.93"`), false},
|
||||
{Host{"147.75.201.93", 9000, true}, []byte(`"147.75.201.93:9000"`), false},
|
||||
{Host{"play12", 0, false}, []byte(`"play12"`), false},
|
||||
{Host{"12play", 0, false}, []byte(`"12play"`), false},
|
||||
{Host{"play-minio-io", 0, false}, []byte(`"play-minio-io"`), false},
|
||||
{Host{"play--minio.io", 0, false}, []byte(`"play--minio.io"`), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
data, err := testCase.host.MarshalJSON()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(data, testCase.expectedData) {
|
||||
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, string(testCase.expectedData), string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedHost *Host
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`""`), &Host{}, false},
|
||||
{[]byte(`"play"`), &Host{"play", 0, false}, false},
|
||||
{[]byte(`"play:0"`), &Host{"play", 0, true}, false},
|
||||
{[]byte(`"play:9000"`), &Host{"play", 9000, true}, false},
|
||||
{[]byte(`"play.minio.io"`), &Host{"play.minio.io", 0, false}, false},
|
||||
{[]byte(`"play.minio.io:9000"`), &Host{"play.minio.io", 9000, true}, false},
|
||||
{[]byte(`"147.75.201.93"`), &Host{"147.75.201.93", 0, false}, false},
|
||||
{[]byte(`"147.75.201.93:9000"`), &Host{"147.75.201.93", 9000, true}, false},
|
||||
{[]byte(`"play12"`), &Host{"play12", 0, false}, false},
|
||||
{[]byte(`"12play"`), &Host{"12play", 0, false}, false},
|
||||
{[]byte(`"play-minio-io"`), &Host{"play-minio-io", 0, false}, false},
|
||||
{[]byte(`"play--minio.io"`), &Host{"play--minio.io", 0, false}, false},
|
||||
{[]byte(`":9000"`), nil, true},
|
||||
{[]byte(`"play:"`), nil, true},
|
||||
{[]byte(`"play::"`), nil, true},
|
||||
{[]byte(`"play:90000"`), nil, true},
|
||||
{[]byte(`"play:-10"`), nil, true},
|
||||
{[]byte(`"play-"`), nil, true},
|
||||
{[]byte(`"play.minio..io"`), nil, true},
|
||||
{[]byte(`":"`), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var host Host
|
||||
err := host.UnmarshalJSON(testCase.data)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(&host, testCase.expectedHost) {
|
||||
t.Fatalf("test %v: host: expected: %#v, got: %#v", i+1, testCase.expectedHost, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHost(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
expectedHost *Host
|
||||
expectErr bool
|
||||
}{
|
||||
{"play", &Host{"play", 0, false}, false},
|
||||
{"play:0", &Host{"play", 0, true}, false},
|
||||
{"play:9000", &Host{"play", 9000, true}, false},
|
||||
{"play.minio.io", &Host{"play.minio.io", 0, false}, false},
|
||||
{"play.minio.io:9000", &Host{"play.minio.io", 9000, true}, false},
|
||||
{"147.75.201.93", &Host{"147.75.201.93", 0, false}, false},
|
||||
{"147.75.201.93:9000", &Host{"147.75.201.93", 9000, true}, false},
|
||||
{"play12", &Host{"play12", 0, false}, false},
|
||||
{"12play", &Host{"12play", 0, false}, false},
|
||||
{"play-minio-io", &Host{"play-minio-io", 0, false}, false},
|
||||
{"play--minio.io", &Host{"play--minio.io", 0, false}, false},
|
||||
{":9000", nil, true},
|
||||
{"play:", nil, true},
|
||||
{"play::", nil, true},
|
||||
{"play:90000", nil, true},
|
||||
{"play:-10", nil, true},
|
||||
{"play-", nil, true},
|
||||
{"play.minio..io", nil, true},
|
||||
{":", nil, true},
|
||||
{"", nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
host, err := ParseHost(testCase.s)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(host, testCase.expectedHost) {
|
||||
t.Fatalf("test %v: host: expected: %#v, got: %#v", i+1, testCase.expectedHost, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParseHost(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
expectedHost *Host
|
||||
}{
|
||||
{"play", &Host{"play", 0, false}},
|
||||
{"play:0", &Host{"play", 0, true}},
|
||||
{"play:9000", &Host{"play", 9000, true}},
|
||||
{"play.minio.io", &Host{"play.minio.io", 0, false}},
|
||||
{"play.minio.io:9000", &Host{"play.minio.io", 9000, true}},
|
||||
{"147.75.201.93", &Host{"147.75.201.93", 0, false}},
|
||||
{"147.75.201.93:9000", &Host{"147.75.201.93", 9000, true}},
|
||||
{"play12", &Host{"play12", 0, false}},
|
||||
{"12play", &Host{"12play", 0, false}},
|
||||
{"play-minio-io", &Host{"play-minio-io", 0, false}},
|
||||
{"play--minio.io", &Host{"play--minio.io", 0, false}},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
host := MustParseHost(testCase.s)
|
||||
|
||||
if !reflect.DeepEqual(host, testCase.expectedHost) {
|
||||
t.Fatalf("test %v: host: expected: %#v, got: %#v", i+1, testCase.expectedHost, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
54
pkg/net/port.go
Normal file
54
pkg/net/port.go
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* 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 net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Port - network port
|
||||
type Port uint16
|
||||
|
||||
// String - returns string representation of port.
|
||||
func (p Port) String() string {
|
||||
return strconv.Itoa(int(p))
|
||||
}
|
||||
|
||||
// ParsePort - parses string into Port
|
||||
func ParsePort(s string) (p Port, err error) {
|
||||
var i int
|
||||
if i, err = strconv.Atoi(s); err != nil {
|
||||
return p, errors.New("invalid port number")
|
||||
}
|
||||
|
||||
if i < 0 || i > 65535 {
|
||||
return p, errors.New("port must be between 0 to 65535")
|
||||
}
|
||||
|
||||
return Port(i), nil
|
||||
}
|
||||
|
||||
// MustParsePort - parses string into Port, else panics
|
||||
func MustParsePort(s string) Port {
|
||||
p, err := ParsePort(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
92
pkg/net/port_test.go
Normal file
92
pkg/net/port_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* 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 net
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPortString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
port Port
|
||||
expectedStr string
|
||||
}{
|
||||
{Port(0), "0"},
|
||||
{Port(9000), "9000"},
|
||||
{Port(65535), "65535"},
|
||||
{Port(1024), "1024"},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
str := testCase.port.String()
|
||||
|
||||
if str != testCase.expectedStr {
|
||||
t.Fatalf("test %v: error: port: %v, got: %v", i+1, testCase.expectedStr, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePort(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
expectedPort Port
|
||||
expectErr bool
|
||||
}{
|
||||
{"0", Port(0), false},
|
||||
{"9000", Port(9000), false},
|
||||
{"65535", Port(65535), false},
|
||||
{"90000", Port(0), true},
|
||||
{"-10", Port(0), true},
|
||||
{"", Port(0), true},
|
||||
{"http", Port(0), true},
|
||||
{" 1024", Port(0), true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
port, err := ParsePort(testCase.s)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if port != testCase.expectedPort {
|
||||
t.Fatalf("test %v: error: port: %v, got: %v", i+1, testCase.expectedPort, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustParsePort(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
expectedPort Port
|
||||
}{
|
||||
{"0", Port(0)},
|
||||
{"9000", Port(9000)},
|
||||
{"65535", Port(65535)},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
port := MustParsePort(testCase.s)
|
||||
|
||||
if port != testCase.expectedPort {
|
||||
t.Fatalf("test %v: error: port: %v, got: %v", i+1, testCase.expectedPort, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
103
pkg/net/url.go
Normal file
103
pkg/net/url.go
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* 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 net
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
// URL - improved JSON friendly url.URL.
|
||||
type URL url.URL
|
||||
|
||||
// IsEmpty - checks URL is empty or not.
|
||||
func (u URL) IsEmpty() bool {
|
||||
return u.String() == ""
|
||||
}
|
||||
|
||||
// String - returns string representation of URL.
|
||||
func (u URL) String() string {
|
||||
// if port number 80 and 443, remove for http and https scheme respectively
|
||||
if u.Host != "" {
|
||||
host := MustParseHost(u.Host)
|
||||
switch {
|
||||
case u.Scheme == "http" && host.Port == 80:
|
||||
fallthrough
|
||||
case u.Scheme == "https" && host.Port == 443:
|
||||
u.Host = host.Name
|
||||
}
|
||||
}
|
||||
|
||||
uu := url.URL(u)
|
||||
return uu.String()
|
||||
}
|
||||
|
||||
// MarshalJSON - converts to JSON string data.
|
||||
func (u URL) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(u.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON - parses given data into URL.
|
||||
func (u *URL) UnmarshalJSON(data []byte) (err error) {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Allow empty string
|
||||
if s == "" {
|
||||
*u = URL{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var ru *URL
|
||||
if ru, err = ParseURL(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u = *ru
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseURL - parses string into URL.
|
||||
func ParseURL(s string) (u *URL, err error) {
|
||||
var uu *url.URL
|
||||
if uu, err = url.Parse(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if uu.Host == "" {
|
||||
if uu.Scheme != "" {
|
||||
return nil, errors.New("scheme appears with empty host")
|
||||
}
|
||||
} else if _, err = ParseHost(uu.Host); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clean path in the URL.
|
||||
// Note: path.Clean() is used on purpose because in MS Windows filepath.Clean() converts
|
||||
// `/` into `\` ie `/foo` becomes `\foo`
|
||||
if uu.Path != "" {
|
||||
uu.Path = path.Clean(uu.Path)
|
||||
}
|
||||
|
||||
v := URL(*uu)
|
||||
u = &v
|
||||
return u, nil
|
||||
}
|
||||
167
pkg/net/url_test.go
Normal file
167
pkg/net/url_test.go
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||
*
|
||||
* 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 net
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestURLIsEmpty(t *testing.T) {
|
||||
testCases := []struct {
|
||||
url URL
|
||||
expectedResult bool
|
||||
}{
|
||||
{URL{}, true},
|
||||
{URL{Scheme: "http", Host: "play"}, false},
|
||||
{URL{Path: "path/to/play"}, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
result := testCase.url.IsEmpty()
|
||||
|
||||
if result != testCase.expectedResult {
|
||||
t.Fatalf("test %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
url URL
|
||||
expectedStr string
|
||||
}{
|
||||
{URL{}, ""},
|
||||
{URL{Scheme: "http", Host: "play"}, "http://play"},
|
||||
{URL{Scheme: "https", Host: "play:443"}, "https://play"},
|
||||
{URL{Scheme: "https", Host: "play.minio.io:80"}, "https://play.minio.io:80"},
|
||||
{URL{Scheme: "https", Host: "147.75.201.93:9000", Path: "/"}, "https://147.75.201.93:9000/"},
|
||||
{URL{Scheme: "https", Host: "s3.amazonaws.com", Path: "/", RawQuery: "location"}, "https://s3.amazonaws.com/?location"},
|
||||
{URL{Scheme: "http", Host: "myminio:10000", Path: "/mybucket/myobject"}, "http://myminio:10000/mybucket/myobject"},
|
||||
{URL{Scheme: "ftp", Host: "myftp.server:10000", Path: "/myuser"}, "ftp://myftp.server:10000/myuser"},
|
||||
{URL{Path: "path/to/play"}, "path/to/play"},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
str := testCase.url.String()
|
||||
|
||||
if str != testCase.expectedStr {
|
||||
t.Fatalf("test %v: string: expected: %v, got: %v", i+1, testCase.expectedStr, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLMarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
url URL
|
||||
expectedData []byte
|
||||
expectErr bool
|
||||
}{
|
||||
{URL{}, []byte(`""`), false},
|
||||
{URL{Scheme: "http", Host: "play"}, []byte(`"http://play"`), false},
|
||||
{URL{Scheme: "https", Host: "play.minio.io:0"}, []byte(`"https://play.minio.io:0"`), false},
|
||||
{URL{Scheme: "https", Host: "147.75.201.93:9000", Path: "/"}, []byte(`"https://147.75.201.93:9000/"`), false},
|
||||
{URL{Scheme: "https", Host: "s3.amazonaws.com", Path: "/", RawQuery: "location"}, []byte(`"https://s3.amazonaws.com/?location"`), false},
|
||||
{URL{Scheme: "http", Host: "myminio:10000", Path: "/mybucket/myobject"}, []byte(`"http://myminio:10000/mybucket/myobject"`), false},
|
||||
{URL{Scheme: "ftp", Host: "myftp.server:10000", Path: "/myuser"}, []byte(`"ftp://myftp.server:10000/myuser"`), false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
data, err := testCase.url.MarshalJSON()
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(data, testCase.expectedData) {
|
||||
t.Fatalf("test %v: data: expected: %v, got: %v", i+1, string(testCase.expectedData), string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedURL *URL
|
||||
expectErr bool
|
||||
}{
|
||||
{[]byte(`""`), &URL{}, false},
|
||||
{[]byte(`"http://play"`), &URL{Scheme: "http", Host: "play"}, false},
|
||||
{[]byte(`"https://play.minio.io:0"`), &URL{Scheme: "https", Host: "play.minio.io:0"}, false},
|
||||
{[]byte(`"https://147.75.201.93:9000/"`), &URL{Scheme: "https", Host: "147.75.201.93:9000", Path: "/"}, false},
|
||||
{[]byte(`"https://s3.amazonaws.com/?location"`), &URL{Scheme: "https", Host: "s3.amazonaws.com", Path: "/", RawQuery: "location"}, false},
|
||||
{[]byte(`"http://myminio:10000/mybucket//myobject/"`), &URL{Scheme: "http", Host: "myminio:10000", Path: "/mybucket/myobject"}, false},
|
||||
{[]byte(`"ftp://myftp.server:10000/myuser"`), &URL{Scheme: "ftp", Host: "myftp.server:10000", Path: "/myuser"}, false},
|
||||
{[]byte(`"myserver:1000"`), nil, true},
|
||||
{[]byte(`"http://:1000/mybucket"`), nil, true},
|
||||
{[]byte(`"https://147.75.201.93:90000/"`), nil, true},
|
||||
{[]byte(`"http:/play"`), nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var url URL
|
||||
err := url.UnmarshalJSON(testCase.data)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(&url, testCase.expectedURL) {
|
||||
t.Fatalf("test %v: host: expected: %#v, got: %#v", i+1, testCase.expectedURL, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseURL(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
expectedURL *URL
|
||||
expectErr bool
|
||||
}{
|
||||
{"http://play", &URL{Scheme: "http", Host: "play"}, false},
|
||||
{"https://play.minio.io:0", &URL{Scheme: "https", Host: "play.minio.io:0"}, false},
|
||||
{"https://147.75.201.93:9000/", &URL{Scheme: "https", Host: "147.75.201.93:9000", Path: "/"}, false},
|
||||
{"https://s3.amazonaws.com/?location", &URL{Scheme: "https", Host: "s3.amazonaws.com", Path: "/", RawQuery: "location"}, false},
|
||||
{"http://myminio:10000/mybucket//myobject/", &URL{Scheme: "http", Host: "myminio:10000", Path: "/mybucket/myobject"}, false},
|
||||
{"ftp://myftp.server:10000/myuser", &URL{Scheme: "ftp", Host: "myftp.server:10000", Path: "/myuser"}, false},
|
||||
{"myserver:1000", nil, true},
|
||||
{"http://:1000/mybucket", nil, true},
|
||||
{"https://147.75.201.93:90000/", nil, true},
|
||||
{"http:/play", nil, true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
url, err := ParseURL(testCase.s)
|
||||
expectErr := (err != nil)
|
||||
|
||||
if expectErr != testCase.expectErr {
|
||||
t.Fatalf("test %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr)
|
||||
}
|
||||
|
||||
if !testCase.expectErr {
|
||||
if !reflect.DeepEqual(url, testCase.expectedURL) {
|
||||
t.Fatalf("test %v: host: expected: %#v, got: %#v", i+1, testCase.expectedURL, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user