github.com/symfony-cli/symfony-cli@v0.0.0-20240514161054-ece2df437dfa/local/proxy/proxy_test.go (about)

     1  /*
     2   * Copyright (c) 2021-present Fabien Potencier <fabien@symfony.com>
     3   *
     4   * This file is part of Symfony CLI project
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU Affero General Public License as
     8   * published by the Free Software Foundation, either version 3 of the
     9   * License, or (at your option) any later version.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    14   * GNU Affero General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU Affero General Public License
    17   * along with this program. If not, see <http://www.gnu.org/licenses/>.
    18   */
    19  
    20  package proxy
    21  
    22  import (
    23  	"crypto/tls"
    24  	"crypto/x509"
    25  	"io"
    26  	"log"
    27  	"net/http"
    28  	"net/http/httptest"
    29  	"net/url"
    30  	"os"
    31  	"path/filepath"
    32  	"strconv"
    33  	"strings"
    34  	"time"
    35  
    36  	"github.com/mitchellh/go-homedir"
    37  	"github.com/rs/zerolog"
    38  	"github.com/symfony-cli/cert"
    39  	"github.com/symfony-cli/symfony-cli/local/pid"
    40  	. "gopkg.in/check.v1"
    41  )
    42  
    43  func (s *ProxySuite) TestProxy(c *C) {
    44  	ca, err := cert.NewCA(filepath.Join("testdata/certs"))
    45  	c.Assert(err, IsNil)
    46  	c.Assert(ca.LoadCA(), IsNil)
    47  
    48  	homedir.Reset()
    49  	os.Setenv("HOME", "testdata")
    50  	defer homedir.Reset()
    51  	defer os.RemoveAll("testdata/.symfony5")
    52  
    53  	p := New(&Config{
    54  		domains: map[string]string{
    55  			"symfony":        "symfony_com",
    56  			"symfony-no-tls": "symfony_com_no_tls",
    57  			"symfony2":       "symfony_com2",
    58  		},
    59  		TLD:  "wip",
    60  		path: "testdata/.symfony5/proxy.json",
    61  	}, ca, log.New(zerolog.New(os.Stderr), "", 0), true)
    62  	os.MkdirAll("testdata/.symfony5", 0755)
    63  	err = p.Save()
    64  	c.Assert(err, IsNil)
    65  
    66  	// Test the 404 fallback
    67  	{
    68  		rr := httptest.NewRecorder()
    69  		req, err := http.NewRequest("GET", "/foo", nil)
    70  		req.Host = "localhost"
    71  		c.Assert(err, IsNil)
    72  		p.proxy.ServeHTTP(rr, req)
    73  		c.Check(rr.Code, Equals, http.StatusNotFound)
    74  	}
    75  
    76  	// Test serving the proxy.pac
    77  	{
    78  		rr := httptest.NewRecorder()
    79  		req, err := http.NewRequest("GET", "/proxy.pac", nil)
    80  		req.Host = "localhost"
    81  		c.Assert(err, IsNil)
    82  		p.proxy.ServeHTTP(rr, req)
    83  		c.Assert(rr.Code, Equals, http.StatusOK)
    84  		c.Check(rr.Header().Get("Content-type"), Equals, "application/x-ns-proxy-autoconfig")
    85  	}
    86  
    87  	// Test serving the index
    88  	{
    89  		rr := httptest.NewRecorder()
    90  		req, err := http.NewRequest("GET", "/", nil)
    91  		req.Host = "localhost"
    92  		c.Assert(err, IsNil)
    93  		p.proxy.ServeHTTP(rr, req)
    94  		c.Assert(rr.Code, Equals, http.StatusOK)
    95  		c.Check(strings.Contains(rr.Body.String(), "symfony.wip"), Equals, true)
    96  	}
    97  
    98  	// Test the proxy
    99  	frontend := httptest.NewServer(p.proxy)
   100  	defer frontend.Close()
   101  	frontendUrl, _ := url.Parse(frontend.URL)
   102  	cert, err := x509.ParseCertificate(ca.AsTLS().Certificate[0])
   103  	c.Assert(err, IsNil)
   104  	certpool := x509.NewCertPool()
   105  	certpool.AddCert(cert)
   106  	transport := &http.Transport{
   107  		Proxy: http.ProxyURL(frontendUrl),
   108  		TLSClientConfig: &tls.Config{
   109  			RootCAs: certpool,
   110  		},
   111  	}
   112  	client := &http.Client{
   113  		Transport: transport,
   114  		Timeout:   1 * time.Second,
   115  	}
   116  
   117  	// Test proxying a request to a non-registered project
   118  	{
   119  		req, _ := http.NewRequest("GET", "https://foo.wip/", nil)
   120  		req.Close = true
   121  
   122  		res, err := client.Do(req)
   123  		c.Assert(err, IsNil)
   124  		c.Assert(res.StatusCode, Equals, http.StatusNotFound)
   125  		body, _ := io.ReadAll(res.Body)
   126  		c.Check(strings.Contains(string(body), "not linked"), Equals, true)
   127  	}
   128  
   129  	// Test proxying a request to a registered project but not started
   130  	{
   131  		req, _ := http.NewRequest("GET", "https://symfony.wip/", nil)
   132  		req.Close = true
   133  
   134  		res, err := client.Do(req)
   135  		c.Assert(err, IsNil)
   136  		c.Assert(res.StatusCode, Equals, http.StatusNotFound)
   137  		body, _ := io.ReadAll(res.Body)
   138  		c.Check(strings.Contains(string(body), "not started"), Equals, true)
   139  	}
   140  	/*
   141  		// Test proxying a request to a registered project and started
   142  		{
   143  			backend := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   144  				w.WriteHeader(200)
   145  				w.Write([]byte(`symfony.wip`))
   146  			}))
   147  			cert, err := ca.CreateCert([]string{"localhost", "127.0.0.1"})
   148  			c.Assert(err, IsNil)
   149  			backend.TLS = &tls.Config{
   150  				Certificates: []tls.Certificate{cert},
   151  			}
   152  			backend.StartTLS()
   153  			defer backend.Close()
   154  			backendURL, err := url.Parse(backend.URL)
   155  			c.Assert(err, IsNil)
   156  
   157  			p := pid.New("symfony_com", nil)
   158  			port, _ := strconv.Atoi(backendURL.Port())
   159  			p.Write(os.Getpid(), port, "https")
   160  
   161  			req, _ := http.NewRequest("GET", "https://symfony.wip/", nil)
   162  			req.Close = true
   163  
   164  			res, err := client.Do(req)
   165  			c.Assert(err, IsNil)
   166  			c.Assert(res.StatusCode, Equals, http.StatusOK)
   167  			body, _ := io.ReadAll(res.Body)
   168  			c.Check(string(body), Equals, "symfony.wip")
   169  		}
   170  	*/
   171  	// Test proxying a request to a registered project but no TLS
   172  	{
   173  		backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   174  			w.WriteHeader(200)
   175  			w.Write([]byte(`http://symfony-no-tls.wip`))
   176  		}))
   177  		defer backend.Close()
   178  		backendURL, err := url.Parse(backend.URL)
   179  		c.Assert(err, IsNil)
   180  
   181  		p := pid.New("symfony_com_no_tls", nil)
   182  		port, _ := strconv.Atoi(backendURL.Port())
   183  		p.Write(os.Getpid(), port, "http")
   184  
   185  		req, _ := http.NewRequest("GET", "http://symfony-no-tls.wip/", nil)
   186  		req.Close = true
   187  
   188  		res, err := client.Do(req)
   189  		c.Assert(err, IsNil)
   190  		body, _ := io.ReadAll(res.Body)
   191  		c.Assert(res.StatusCode, Equals, http.StatusOK)
   192  		c.Assert(string(body), Equals, "http://symfony-no-tls.wip")
   193  	}
   194  
   195  	// Test proxying a request to an outside backend
   196  	{
   197  		backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   198  			w.WriteHeader(200)
   199  		}))
   200  		defer backend.Close()
   201  		req, _ := http.NewRequest("GET", backend.URL, nil)
   202  		req.Close = true
   203  
   204  		res, err := client.Do(req)
   205  		c.Assert(err, IsNil)
   206  		c.Assert(res.StatusCode, Equals, http.StatusOK)
   207  	}
   208  	/*
   209  		// Test proxying a request over HTTP2
   210  		http2.ConfigureTransport(transport)
   211  		{
   212  			backend := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   213  				w.WriteHeader(200)
   214  				if r.Proto == "HTTP/2.0" {
   215  					w.Write([]byte(`http2`))
   216  					return
   217  				}
   218  				w.Write([]byte(`symfony.wip`))
   219  			}))
   220  			cert, err := ca.CreateCert([]string{"localhost", "127.0.0.1"})
   221  			c.Assert(err, IsNil)
   222  			backend.TLS = &tls.Config{
   223  				Certificates: []tls.Certificate{cert},
   224  				NextProtos:   []string{"h2", "http/1.1"},
   225  			}
   226  			backend.StartTLS()
   227  			defer backend.Close()
   228  			backendURL, err := url.Parse(backend.URL)
   229  			c.Assert(err, IsNil)
   230  
   231  			p := pid.New("symfony_com2", nil)
   232  			port, _ := strconv.Atoi(backendURL.Port())
   233  			p.Write(os.Getpid(), port, "https")
   234  
   235  			req, _ := http.NewRequest("GET", "https://symfony2.wip/", nil)
   236  			req.Close = true
   237  
   238  			res, err := client.Do(req)
   239  			c.Assert(err, IsNil)
   240  			c.Assert(res.StatusCode, Equals, http.StatusOK)
   241  			body, _ := ioutil.ReadAll(res.Body)
   242  			c.Check(string(body), Equals, "http2")
   243  		}
   244  	*/
   245  }