11package tools
22
33import (
4+ "compress/gzip"
45 "fmt"
6+ "io"
57 "io/ioutil"
68 "log"
79 "net/http"
@@ -11,9 +13,22 @@ import (
1113 "strings"
1214)
1315
16+ var originalUrlResolver = make (map [string ]* url.URL )
17+
1418// ProxyRequestHandler intercepts requests to CodeArtifact and add the Authorization header + correct Host header
1519func ProxyRequestHandler (p * httputil.ReverseProxy ) func (http.ResponseWriter , * http.Request ) {
1620 return func (w http.ResponseWriter , r * http.Request ) {
21+ // Store the original host header for each request
22+ originalUrlResolver [r .RemoteAddr ] = r .URL
23+ originalUrlResolver [r .RemoteAddr ].Host = r .Host
24+ originalUrlResolver [r .RemoteAddr ].Scheme = r .URL .Scheme
25+
26+ if r .Header .Get ("X-Forwarded-Proto" ) == "https" {
27+ originalUrlResolver [r .RemoteAddr ].Scheme = "https"
28+ } else {
29+ originalUrlResolver [r .RemoteAddr ].Scheme = "http"
30+ }
31+
1732 // Override the Host header with the CodeArtifact Host
1833 u , _ := url .Parse (CodeArtifactAuthInfo .Url )
1934 r .Host = u .Host
@@ -31,33 +46,57 @@ func ProxyRequestHandler(p *httputil.ReverseProxy) func(http.ResponseWriter, *ht
3146
3247func ProxyResponseHandler () func (* http.Response ) error {
3348 return func (r * http.Response ) error {
49+ log .Printf ("Received response from %s" , r .Request .URL .String ())
3450 log .Printf ("RES: %s \" %s\" %d \" %s\" \" %s\" " , r .Request .RemoteAddr , r .Request .Method , r .StatusCode , r .Request .RequestURI , r .Request .UserAgent ())
3551
3652 contentType := r .Header .Get ("Content-Type" )
37- log .Printf ("%s" , contentType )
53+
54+ originalUrl := originalUrlResolver [r .Request .RemoteAddr ]
55+ delete (originalUrlResolver , r .Request .RemoteAddr )
56+
57+ u , _ := url .Parse (CodeArtifactAuthInfo .Url )
58+ hostname := u .Host + ":443"
59+
60+ // Rewrite the 301 to point from CodeArtifact URL to the proxy instead..
61+ if r .StatusCode == 301 || r .StatusCode == 302 {
62+ location , _ := r .Location ()
63+
64+ location .Host = originalUrl .Host
65+ location .Scheme = originalUrl .Scheme
66+ location .Path = strings .Replace (location .Path , u .Path , "" , 1 )
67+
68+ r .Header .Set ("Location" , location .String ())
69+ }
3870
3971 // Do some quick fixes to the HTTP response for NPM install requests
4072 // TODO: Get this actually working, it looks like the JSON responses provide the correct URLs via CURL, but not when using npm against it.
4173 if strings .HasPrefix (r .Request .UserAgent (), "npm" ) {
42- if ! strings .Contains (contentType , "application/json" ) {
74+
75+ // Respond to only requests that respond with JSON
76+ // There might eventually be additional headers i don't know about?
77+ if ! strings .Contains (contentType , "application/json" ) && ! strings .Contains (contentType , "application/vnd.npm.install-v1+json" ) {
4378 return nil
4479 }
4580
81+ var body io.ReadCloser
82+
83+ if r .Header .Get ("Content-Encoding" ) == "gzip" {
84+ body , _ = gzip .NewReader (r .Body )
85+ r .Header .Del ("Content-Encoding" )
86+ } else {
87+ body = r .Body
88+ }
89+
4690 // replace any instances of the CodeArtifact URL with the local URL
47- oldContentResponse , _ := ioutil .ReadAll (r . Body )
91+ oldContentResponse , _ := ioutil .ReadAll (body )
4892 oldContentResponseStr := string (oldContentResponse )
4993
50- u , _ := url .Parse (CodeArtifactAuthInfo .Url )
51- hostname := u .Host + ":443"
52-
5394 resolvedHostname := strings .Replace (CodeArtifactAuthInfo .Url , u .Host , hostname , - 1 )
54- newUrl := fmt .Sprintf ("http ://%s/" , "localhost" )
95+ newUrl := fmt .Sprintf ("%s ://%s/" , originalUrl . Scheme , originalUrl . Host )
5596
5697 newResponseContent := strings .Replace (oldContentResponseStr , resolvedHostname , newUrl , - 1 )
5798 newResponseContent = strings .Replace (newResponseContent , CodeArtifactAuthInfo .Url , newUrl , - 1 )
5899
59- log .Print (newResponseContent )
60-
61100 r .Body = ioutil .NopCloser (strings .NewReader (newResponseContent ))
62101 r .ContentLength = int64 (len (newResponseContent ))
63102 r .Header .Set ("Content-Length" , strconv .Itoa (len (newResponseContent )))
0 commit comments