Repository: go
Issue: net/url: make URL parsing return an error on IPv6 literal without brackets
Labels: help wanted,NeedsFix

<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of Go are you using (`go version`)?

<pre>
go version go1.11.2 windows/amd64
</pre>

### Does this issue reproduce with the latest release?
<pre>
yes
</pre>



### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
set GOARCH=amd64
set GOHOSTARCH=amd64
set GOHOSTOS=windows
</pre></details>

### What did you do?

url.URL provides the methods `.Hostname()` and `.Port()` which schould split the URL's host (`.Host`) into its address and port part. In certain cases, these functions are not able to interpret IPv6 addresses correctly, leading to invalid output.  

Here is a test function feeding different sample URLs, demonstrating the issue (**all test URLs are valid and should succeed!**):

```
func TestUrl(t *testing.T) {
	tests := []struct {
		name        string
		input string
		wantHost     string
		wantPort string
	}{
		{"domain-unknonw-scheme", "asfd://localhost/?q=0&p=1#frag", "localhost", ""},
		{"domain-implicit", "http://localhost/?q=0&p=1#frag", "localhost", ""},
		{"domain-explicit", "http://localhost:80/?q=0&p=1#frag", "localhost", "80"},
		{"domain-explicit-other-port", "http://localhost:80/?q=0&p=1#frag", "localhost", "80"},

		{"ipv4-implicit", "http://127.0.0.1/?q=0&p=1#frag", "127.0.0.1", ""},
		{"ipv4-explicit", "http://127.0.0.1:80/?q=0&p=1#frag", "127.0.0.1", "80"},
		{"ipv4-explicit-other-port", "http://127.0.0.1:80/?q=0&p=1#frag", "127.0.0.1", "80"},

		{"ipv6-explicit", "http://[1::]:80/?q=0&p=1#frag", "1::", "80"},
		{"ipv6-explicit-other-port", "http://[1::]:80/?q=0&p=1#frag", "1::", "80"},

		{"ipv6-implicit-1", "http://[1::]/?q=0&p=1#frag", "1::", ""},

		{"ipv6-implicit-2", "http://1::/?q=0&p=1#frag", "1::", ""},
		{"ipv6-implicit-3", "http://1::2008/?q=0&p=1#frag", "1::2008", ""},
		{"ipv6-implicit-4", "http://1::2008:1/?q=0&p=1#frag", "1::2008:1", ""},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Prepare URL
			u, err := url.Parse(tt.input)
			if err != nil {
				t.Errorf("could not parse url: %v", err)
				return
			}

			// Extract hostname and port
			host := u.Hostname()
			port := u.Port()

			// Compare result
			if host != tt.wantHost {
				t.Errorf("TestUrl() got = %v, want %v", host, tt.wantHost)
			}
			if port != tt.wantPort {
				t.Errorf("TestUrl() got1 = %v, want %v", port, tt.wantPort)
			}
		})
	}
}
```

Output:
![grafik](https://user-images.githubusercontent.com/31357019/54908236-bace3980-4ee7-11e9-96f4-1c9aa2d83213.png)



### What did you expect to see?
All sample URLs in the test cases above are valid ones, hence, all tests should succeed as defined

### What did you see instead?
The top test samples work as expected, however, the bottom three return incorrect results. The bottom three samples are valid IPv6 URLs with inplicit port specification, but `.Hostname()` and `.Port()` interpres them as IPv4 addresses returning parts of the IPv6 address as if it was the explicit port of an IPv4 input. E.g., in one of the test outputs, ":2008" is returned as the port, but it is actually part of the IPv6 address.

### Where is the bug?
 `.Hostname()` and `.Port()` implement their own logic to split the port from the hostname. I've found that there is already a close function in the net package, called `net.SplitHostPort()`, which does it's job correctly. If  `.Hostname()` and `.Port()` just called that function instead of re-implementing the logic, everything should work as expected. Below is the proof in form of a test function with exactly the same inputs as above, but using `net.SplitHostPort()` instead of  `.Hostname()` / `.Port()`. 


```
func TestUrlNetSplit(t *testing.T) {
	tests := []struct {
		name        string
		input string
		wantHost     string
		wantPort string
	}{
		{"domain-unknonw-scheme", "asfd://localhost/?q=0&p=1#frag", "localhost", ""},
		{"domain-implicit", "http://localhost/?q=0&p=1#frag", "localhost", ""},
		{"domain-explicit", "http://localhost:80/?q=0&p=1#frag", "localhost", "80"},
		{"domain-explicit-other-port", "http://localhost:80/?q=0&p=1#frag", "localhost", "80"},

		{"ipv4-implicit", "http://127.0.0.1/?q=0&p=1#frag", "127.0.0.1", ""},
		{"ipv4-explicit", "http://127.0.0.1:80/?q=0&p=1#frag", "127.0.0.1", "80"},
		{"ipv4-explicit-other-port", "http://127.0.0.1:80/?q=0&p=1#frag", "127.0.0.1", "80"},

		{"ipv6-explicit", "http://[1::]:80/?q=0&p=1#frag", "1::", "80"},
		{"ipv6-explicit-other-port", "http://[1::]:80/?q=0&p=1#frag", "1::", "80"},

		{"ipv6-implicit-1", "http://[1::]/?q=0&p=1#frag", "1::", ""},

		{"ipv6-implicit-2", "http://1::/?q=0&p=1#frag", "1::", ""},
		{"ipv6-implicit-3", "http://1::2008/?q=0&p=1#frag", "1::2008", ""},
		{"ipv6-implicit-4", "http://1::2008:1/?q=0&p=1#frag", "1::2008:1", ""},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Prepare URL
			u, err := url.Parse(tt.input)
			if err != nil {
				t.Errorf("could not parse url: %v", err)
				return
			}

			host, port, err := net.SplitHostPort(u.Host)
			if err != nil {
				// Could not split port from host, as there is no port specified
				host = u.Host
				port = ""
			}

			// Compare result
			if host != tt.wantHost {
				t.Errorf("TestUrl() got = %v, want %v", host, tt.wantHost)
			}
			if port != tt.wantPort {
				t.Errorf("TestUrl() got1 = %v, want %v", port, tt.wantPort)
			}
		})
	}
}
```

Output:
![grafik](https://user-images.githubusercontent.com/31357019/54908563-7f803a80-4ee8-11e9-8719-a190ed8f407a.png)