@@ -8,15 +8,19 @@ import (
88 "context"
99 "fmt"
1010 "io"
11+ "io/ioutil"
1112 "net/http"
1213 "net/url"
14+ "os"
1315 "path"
16+ "path/filepath"
1417 "regexp"
18+ "runtime"
1519 "strconv"
1620 "strings"
1721 "sync"
1822
19- "github.com/armon/go-radix"
23+ radix "github.com/armon/go-radix"
2024 "github.com/pkg/errors"
2125)
2226
2630 hgSchemes = []string {"https" , "ssh" , "http" }
2731 svnSchemes = []string {"https" , "http" , "svn" , "svn+ssh" }
2832 gopkginSchemes = []string {"https" , "http" }
33+ netrc []netrcLine
34+ readNetrcOnce sync.Once
2935)
3036
3137const gopkgUnstableSuffix = "-unstable"
@@ -848,6 +854,8 @@ func doFetchMetadata(ctx context.Context, scheme, path string) (io.ReadCloser, e
848854 return nil , errors .Wrapf (err , "unable to build HTTP request for URL %q" , url )
849855 }
850856
857+ req = addAuthFromNetrc (url , req )
858+
851859 resp , err := http .DefaultClient .Do (req .WithContext (ctx ))
852860 if err != nil {
853861 return nil , errors .Wrapf (err , "failed HTTP request to URL %q" , url )
@@ -859,6 +867,113 @@ func doFetchMetadata(ctx context.Context, scheme, path string) (io.ReadCloser, e
859867 }
860868}
861869
870+ // See https://github.com/golang/go/blob/master/src/cmd/go/internal/web2/web.go
871+ // for implementation
872+ // Temporary netrc reader until https://github.com/golang/go/issues/31334 is solved
873+ type netrcLine struct {
874+ machine string
875+ login string
876+ password string
877+ }
878+
879+ func parseNetrc (data string ) []netrcLine {
880+ // See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
881+ // for documentation on the .netrc format.
882+ var nrc []netrcLine
883+ var l netrcLine
884+ inMacro := false
885+ for _ , line := range strings .Split (data , "\n " ) {
886+ if inMacro {
887+ if line == "" {
888+ inMacro = false
889+ }
890+ continue
891+ }
892+
893+ f := strings .Fields (line )
894+ i := 0
895+ for ; i < len (f )- 1 ; i += 2 {
896+ // Reset at each "machine" token.
897+ // “The auto-login process searches the .netrc file for a machine token
898+ // that matches […]. Once a match is made, the subsequent .netrc tokens
899+ // are processed, stopping when the end of file is reached or another
900+ // machine or a default token is encountered.”
901+ switch f [i ] {
902+ case "machine" :
903+ l = netrcLine {machine : f [i + 1 ]}
904+ case "login" :
905+ l .login = f [i + 1 ]
906+ case "password" :
907+ l .password = f [i + 1 ]
908+ case "macdef" :
909+ // “A macro is defined with the specified name; its contents begin with
910+ // the next .netrc line and continue until a null line (consecutive
911+ // new-line characters) is encountered.”
912+ inMacro = true
913+ }
914+ if l .machine != "" && l .login != "" && l .password != "" {
915+ nrc = append (nrc , l )
916+ l = netrcLine {}
917+ }
918+ }
919+
920+ if i < len (f ) && f [i ] == "default" {
921+ // “There can be only one default token, and it must be after all machine tokens.”
922+ break
923+ }
924+ }
925+
926+ return nrc
927+ }
928+
929+ func netrcPath () (string , error ) {
930+ if env := os .Getenv ("NETRC" ); env != "" {
931+ return env , nil
932+ }
933+
934+ dir := os .Getenv ("HOME" )
935+
936+ base := ".netrc"
937+ if runtime .GOOS == "windows" {
938+ base = "_netrc"
939+ }
940+ return filepath .Join (dir , base ), nil
941+ }
942+
943+ // readNetrc parses a user's netrc file, ignoring any errors that occur.
944+ func readNetrc () {
945+ path , err := netrcPath ()
946+ if err != nil {
947+ return
948+ }
949+
950+ data , err := ioutil .ReadFile (path )
951+ if err != nil {
952+ return
953+ }
954+
955+ netrc = parseNetrc (string (data ))
956+ }
957+
958+ // addAuthFromNetrc uses basic authentication on go-get requests
959+ // for private repositories.
960+ func addAuthFromNetrc (rawurl string , req * http.Request ) * http.Request {
961+ readNetrcOnce .Do (readNetrc )
962+ for _ , m := range netrc {
963+ u , err := url .Parse (rawurl )
964+ if err != nil {
965+ continue
966+ }
967+
968+ if u .Host == m .machine {
969+ req .SetBasicAuth (m .login , m .password )
970+ break
971+ }
972+ }
973+
974+ return req
975+ }
976+
862977// getMetadata fetches and decodes remote metadata for path.
863978//
864979// scheme is optional. If it's http, only http will be attempted for fetching.
0 commit comments