@@ -8,15 +8,19 @@ import (
8
8
"context"
9
9
"fmt"
10
10
"io"
11
+ "io/ioutil"
11
12
"net/http"
12
13
"net/url"
14
+ "os"
13
15
"path"
16
+ "path/filepath"
14
17
"regexp"
18
+ "runtime"
15
19
"strconv"
16
20
"strings"
17
21
"sync"
18
22
19
- "github.com/armon/go-radix"
23
+ radix "github.com/armon/go-radix"
20
24
"github.com/pkg/errors"
21
25
)
22
26
26
30
hgSchemes = []string {"https" , "ssh" , "http" }
27
31
svnSchemes = []string {"https" , "http" , "svn" , "svn+ssh" }
28
32
gopkginSchemes = []string {"https" , "http" }
33
+ netrc []netrcLine
34
+ readNetrcOnce sync.Once
29
35
)
30
36
31
37
const gopkgUnstableSuffix = "-unstable"
@@ -848,6 +854,8 @@ func doFetchMetadata(ctx context.Context, scheme, path string) (io.ReadCloser, e
848
854
return nil , errors .Wrapf (err , "unable to build HTTP request for URL %q" , url )
849
855
}
850
856
857
+ req = addAuthFromNetrc (url , req )
858
+
851
859
resp , err := http .DefaultClient .Do (req .WithContext (ctx ))
852
860
if err != nil {
853
861
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
859
867
}
860
868
}
861
869
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
+
862
977
// getMetadata fetches and decodes remote metadata for path.
863
978
//
864
979
// scheme is optional. If it's http, only http will be attempted for fetching.
0 commit comments