github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/utils/tests/proxy/server/server.go (about) 1 package server 2 3 import ( 4 "crypto/tls" 5 "io" 6 "log" 7 "net" 8 "net/http" 9 "net/http/httputil" 10 "net/url" 11 "os" 12 "path/filepath" 13 14 "github.com/jfrog/jfrog-cli-go/utils/tests" 15 "github.com/jfrog/jfrog-cli-go/utils/tests/proxy/server/certificate" 16 "github.com/jfrog/jfrog-client-go/utils" 17 clilog "github.com/jfrog/jfrog-client-go/utils/log" 18 ) 19 20 type httpResponse func(rw http.ResponseWriter, req *http.Request) 21 22 func handleReverseProxyHttps(reverseProxy *httputil.ReverseProxy) httpResponse { 23 return func(rw http.ResponseWriter, req *http.Request) { 24 clilog.Info("*********************************************************") 25 clilog.Info("Scheme: ", "HTTPS") 26 clilog.Info("Host: ", req.Host) 27 clilog.Info("Method: ", req.Method) 28 clilog.Info("URI: ", req.RequestURI) 29 clilog.Info("Agent: ", req.UserAgent()) 30 clilog.Info("*********************************************************") 31 reverseProxy.ServeHTTP(rw, req) 32 } 33 } 34 35 func getReverseProxyHandler(targetUrl string) (*httputil.ReverseProxy, error) { 36 clilog.Info("Reverse proxy URL:", targetUrl) 37 var err error 38 var target *url.URL 39 target, err = url.Parse(targetUrl) 40 if err != nil { 41 return nil, err 42 } 43 origHost := target.Host 44 d := func(req *http.Request) { 45 req.URL.Host = origHost 46 req.Host = origHost 47 req.URL.Scheme = target.Scheme 48 } 49 tr := &http.Transport{ 50 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 51 } 52 proxyErrLogger := log.New(os.Stdout, "PROXY-LOGGER", log.Ldate|log.Ltime|log.Lshortfile) 53 p := &httputil.ReverseProxy{Director: d, Transport: tr, ErrorLog: proxyErrLogger} 54 return p, nil 55 } 56 57 func removeProxyHeaders(r *http.Request) { 58 r.RequestURI = "" // this must be reset when serving a request with the client 59 // If no Accept-Encoding header exists, Transport will add the headers it can accept 60 // and would wrap the response body with the relevant reader. 61 r.Header.Del("Accept-Encoding") 62 // curl can add that, see 63 // https://jdebp.eu./FGA/web-proxy-connection-header.html 64 r.Header.Del("Proxy-Connection") 65 r.Header.Del("Proxy-Authenticate") 66 r.Header.Del("Proxy-Authorization") 67 // Connection, Authenticate and Authorization are single hop Header: 68 // http://www.w3.org/Protocols/rfc2616/rfc2616.txt 69 // 14.10 Connection 70 // The Connection general-header field allows the sender to specify 71 // options that are desired for that particular connection and MUST NOT 72 // be communicated by proxies over further connections. 73 r.Header.Del("Connection") 74 } 75 76 func copyHeaders(dst, src http.Header) { 77 for k := range dst { 78 dst.Del(k) 79 } 80 for k, vs := range src { 81 for _, v := range vs { 82 dst.Add(k, v) 83 } 84 } 85 } 86 87 func httpProxyHandler(w http.ResponseWriter, r *http.Request) { 88 if r.RequestURI == "/" { 89 w.WriteHeader(200) 90 } else { 91 removeProxyHeaders(r) 92 t := &http.Transport{} 93 resp, err := t.RoundTrip(r) 94 if err != nil { 95 clilog.Error(err) 96 } 97 origBody := resp.Body 98 defer origBody.Close() 99 copyHeaders(w.Header(), resp.Header) 100 w.WriteHeader(resp.StatusCode) 101 _, err = io.Copy(w, resp.Body) 102 if err := resp.Body.Close(); err != nil { 103 clilog.Error("Can't close response body %v", err) 104 } 105 } 106 } 107 108 type testProxy struct { 109 } 110 111 func (t *testProxy) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) { 112 if request.RequestURI == "/" { 113 responseWriter.WriteHeader(200) 114 } else { 115 host := request.URL.Host 116 request.Host = "https://" + request.Host 117 targetSiteCon, err := net.Dial("tcp", host) 118 if err != nil { 119 clilog.Error(err) 120 return 121 } 122 hij, ok := responseWriter.(http.Hijacker) 123 if !ok { 124 http.Error(responseWriter, "webserver doesn't support hijacking", http.StatusInternalServerError) 125 return 126 } 127 proxyClient, _, err := hij.Hijack() 128 if err != nil { 129 http.Error(responseWriter, err.Error(), http.StatusInternalServerError) 130 return 131 } 132 133 proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) 134 targetTCP, targetOK := targetSiteCon.(*net.TCPConn) 135 proxyClientTCP, clientOK := proxyClient.(*net.TCPConn) 136 if targetOK && clientOK { 137 go copyAndClose(targetTCP, proxyClientTCP) 138 go copyAndClose(proxyClientTCP, targetTCP) 139 } 140 } 141 } 142 143 func copyAndClose(dst, src *net.TCPConn) { 144 if _, err := io.Copy(dst, src); err != nil { 145 clilog.Error(err) 146 } 147 dst.CloseWrite() 148 src.CloseRead() 149 } 150 151 func GetProxyHttpPort() string { 152 port := "8099" 153 if httpPort := os.Getenv("PROXY_HTTP_PORT"); httpPort != "" { 154 port = httpPort 155 } 156 return port 157 } 158 159 func prepareHTTPSHandling(handler *httputil.ReverseProxy) (*http.ServeMux, string, string) { 160 // We can use the same handler for both HTTP and HTTPS 161 httpsMux := http.NewServeMux() 162 httpsMux.HandleFunc("/", handleReverseProxyHttps(handler)) 163 absPathCert, absPathKey := CreateNewServerCertificates() 164 return httpsMux, absPathCert, absPathKey 165 } 166 167 // Create new server certificates and returns the certificates path 168 func CreateNewServerCertificates() (string, string) { 169 if _, err := os.Stat(certificate.CERT_FILE); os.IsNotExist(err) { 170 certificate.CreateNewCert() 171 } 172 absPathCert, _ := filepath.Abs(certificate.CERT_FILE) 173 absPathKey, _ := filepath.Abs(certificate.KEY_FILE) 174 return absPathCert, absPathKey 175 } 176 177 func GetProxyHttpsPort() string { 178 port := "1024" 179 if httpPort := os.Getenv(tests.HttpsProxyEnvVar); httpPort != "" { 180 port = httpPort 181 } 182 return port 183 } 184 185 func startHttpsReverseProxy(proxyTarget string, requestClientCerts bool) { 186 handler, err := getReverseProxyHandler(proxyTarget) 187 if err != nil { 188 panic(err) 189 } 190 // Starts a new Go routine 191 httpsMux, absPathCert, absPathKey := prepareHTTPSHandling(handler) 192 193 if requestClientCerts { 194 server := &http.Server{ 195 Addr: ":" + GetProxyHttpsPort(), 196 Handler: httpsMux, 197 TLSConfig: &tls.Config{ 198 ClientAuth: tls.RequireAnyClientCert, 199 }, 200 } 201 202 server.ListenAndServeTLS(absPathCert, absPathKey) 203 } else { 204 err = http.ListenAndServeTLS(":"+GetProxyHttpsPort(), absPathCert, absPathKey, httpsMux) 205 if err != nil { 206 panic(err) 207 } 208 } 209 } 210 211 func StartLocalReverseHttpProxy(artifactoryUrl string, requestClientCerts bool) { 212 if artifactoryUrl == "" { 213 artifactoryUrl = "http://localhost:8081/artifactory/" 214 } 215 artifactoryUrl = utils.AddTrailingSlashIfNeeded(artifactoryUrl) 216 startHttpsReverseProxy(artifactoryUrl, requestClientCerts) 217 } 218 219 func StartHttpProxy() { 220 httpMux := http.NewServeMux() 221 httpMux.HandleFunc("/", httpProxyHandler) 222 port := GetProxyHttpPort() 223 err := http.ListenAndServe(":"+port, httpMux) 224 if err != nil { 225 panic(err) 226 } 227 } 228 229 func StartHttpsProxy() { 230 port := GetProxyHttpsPort() 231 err := http.ListenAndServe(":"+port, &testProxy{}) 232 if err != nil { 233 panic(err) 234 } 235 }