lib/proxy: send the decoded user and password in Proxy-Authorization
HTTPConnectDial built the Basic credential from url.Userinfo.String(), which percent-encodes the user name and password. A proxy URL such as http://me%40example.com:p%40ss@proxy:3128 sent "me%40example.com:p%40ss" instead of "me@example.com:p@ss" and the proxy refused it. Use Username() and Password() like net/http does, which also always puts the ":" separator in.
This commit is contained in:
1 parent
d1c54c4e5d
commit
5c143c89bf
2 files changed
+55
-1
No files matched your search
+4
-1
@@ -79,7 +79,10 @@ func HTTPConnectDial(network, addr string, proxyURL *url.URL, proxyDialer proxy.
|
||||
// send CONNECT
|
||||
user := proxyURL.User
|
||||
if user != nil {
|
||||
credential := base64.StdEncoding.EncodeToString([]byte(user.String()))
|
||||
// Use the decoded user name and password - user.String() would
|
||||
// send them percent-encoded (eg "me%40example.com").
|
||||
password, _ := user.Password()
|
||||
credential := base64.StdEncoding.EncodeToString([]byte(user.Username() + ":" + password))
|
||||
_, err = fmt.Fprintf(conn, "CONNECT %s HTTP/1.1\r\nHost: %s\r\nProxy-Authorization: Basic %s\r\n\r\n", addr, addr, credential)
|
||||
} else {
|
||||
_, err = fmt.Fprintf(conn, "CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", addr, addr)
|
||||
|
||||
@@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -118,3 +119,53 @@ func TestHTTPConnectDialNon200(t *testing.T) {
|
||||
assert.Nil(t, conn)
|
||||
assert.Contains(t, err.Error(), "403")
|
||||
}
|
||||
|
||||
// Check that the Proxy-Authorization header carries the decoded user
|
||||
// name and password, not their percent-encoded form.
|
||||
func TestHTTPConnectDialAuth(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
userInfo string
|
||||
want string
|
||||
}{
|
||||
{userInfo: "user:pass", want: "user:pass"},
|
||||
{userInfo: "me%40example.com:p%40ss%3Aword", want: "me@example.com:p@ss:word"},
|
||||
{userInfo: "user", want: "user:"},
|
||||
} {
|
||||
t.Run(test.userInfo, func(t *testing.T) {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = listener.Close()
|
||||
}()
|
||||
authHeader := make(chan string, 1)
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
br := bufio.NewReader(conn)
|
||||
auth := ""
|
||||
for {
|
||||
line, err := br.ReadString('\n')
|
||||
if err != nil || line == "\r\n" || line == "\n" {
|
||||
break
|
||||
}
|
||||
if value, ok := strings.CutPrefix(line, "Proxy-Authorization: "); ok {
|
||||
auth = strings.TrimSpace(value)
|
||||
}
|
||||
}
|
||||
authHeader <- auth
|
||||
_, _ = conn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
}()
|
||||
proxyURL, err := url.Parse("http://" + test.userInfo + "@" + listener.Addr().String())
|
||||
require.NoError(t, err)
|
||||
conn, err := HTTPConnectDial("tcp", "example.com:1234", proxyURL, nil)
|
||||
require.NoError(t, err)
|
||||
_ = conn.Close()
|
||||
assert.Equal(t, "Basic "+base64.StdEncoding.EncodeToString([]byte(test.want)), <-authHeader)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user