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  }