github.com/cmars/oniongrok@v0.4.0/forwarding/service_test.go (about) 1 package forwarding_test 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "net/http/httptest" 10 "net/url" 11 "os" 12 "strconv" 13 "testing" 14 "time" 15 16 qt "github.com/frankban/quicktest" 17 "golang.org/x/net/context/ctxhttp" 18 19 "github.com/cmars/oniongrok/config" 20 "github.com/cmars/oniongrok/forwarding" 21 "github.com/cmars/oniongrok/tor" 22 ) 23 24 const skipForwardingTests = "SKIP_FORWARDING_TESTS" 25 26 var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 27 _, err := w.Write([]byte("hello world")) 28 if err != nil { 29 panic(err) 30 } 31 }) 32 33 const testTimeout = 300 * time.Second 34 const forwardTimeout = 60 * time.Second 35 const clientTimeout = 60 * time.Second 36 const closeTimeout = 30 * time.Second 37 38 // Tor Browser User Manual 39 const importTestHost = "dsbqrprgkqqifztta6h3w7i2htjhnq7d3qkh3c7gvc35e66rrcv66did.onion" 40 41 func TestIntegration(t *testing.T) { 42 c := qt.New(t) 43 if os.Getenv(skipForwardingTests) != "" { 44 c.Skip() 45 } 46 47 // Setup 48 wd := c.Mkdir() 49 c.Assert(os.Chdir(wd), qt.IsNil) 50 51 // A local server we're going to export 52 srv := httptest.NewServer(testHandler) 53 c.Cleanup(srv.Close) 54 55 unixSrv := httptest.NewUnstartedServer(testHandler) 56 unixListener, err := net.Listen("unix", wd+"/test.sock") 57 c.Assert(err, qt.IsNil) 58 c.Cleanup(func() { unixListener.Close() }) 59 unixSrv.Listener = unixListener 60 unixSrv.Start() 61 c.Cleanup(srv.Close) 62 63 // Find a likely open port for importing a remote server 64 l, err := net.Listen("tcp4", "127.0.0.1:0") 65 c.Assert(err, qt.IsNil) 66 importPort := l.Addr().(*net.TCPAddr).Port 67 c.Assert(l.Close(), qt.IsNil) 68 c.Assert(importPort, qt.Not(qt.Equals), 0) 69 70 ctx, cancel := context.WithTimeout(context.Background(), testTimeout) 71 c.Cleanup(cancel) 72 73 torSvc, err := tor.Start(ctx, tor.Debug(os.Stderr)) 74 c.Assert(err, qt.IsNil) 75 c.Cleanup(func() { 76 // TODO: improve bine to support a context on Close 77 ch := make(chan struct{}) 78 go func() { 79 err := torSvc.Close() 80 c.Assert(err, qt.IsNil) 81 close(ch) 82 }() 83 select { 84 case <-ch: 85 case <-time.After(closeTimeout): 86 c.Log("failed to shut down Tor -- possible bug in bine") 87 } 88 }) 89 90 srvURL, err := url.Parse(srv.URL) 91 c.Assert(err, qt.IsNil) 92 exportHost, exportPort, err := net.SplitHostPort(srvURL.Host) 93 c.Assert(err, qt.IsNil) 94 exportPortNum, err := strconv.Atoi(exportPort) 95 c.Assert(err, qt.IsNil) 96 exportDoc := config.ForwardDoc{ 97 Src: config.EndpointDoc{ 98 Host: exportHost, 99 Ports: []int{exportPortNum}, 100 }, 101 Dest: config.EndpointDoc{ 102 Ports: []int{80}, 103 }, 104 } 105 exportFwd, err := exportDoc.Forward() 106 c.Assert(err, qt.IsNil) 107 exportUnixDoc := config.ForwardDoc{ 108 Src: config.EndpointDoc{ 109 Path: wd + "/test.sock", 110 }, 111 Dest: config.EndpointDoc{ 112 Ports: []int{81}, 113 }, 114 } 115 exportUnixFwd, err := exportUnixDoc.Forward() 116 c.Assert(err, qt.IsNil) 117 118 importDoc := config.ForwardDoc{ 119 Src: config.EndpointDoc{ 120 // Tor Browser User Manual 121 Host: importTestHost, 122 Ports: []int{80}, 123 }, 124 Dest: config.EndpointDoc{ 125 Ports: []int{importPort}, 126 }, 127 } 128 importFwd, err := importDoc.Forward() 129 c.Assert(err, qt.IsNil) 130 131 fwdSvc := forwarding.New(torSvc, exportFwd, exportUnixFwd, importFwd) 132 133 fwdCtx, cancel := context.WithTimeout(ctx, forwardTimeout) 134 c.Cleanup(cancel) 135 onionIDs, err := fwdSvc.Start(fwdCtx) 136 c.Assert(err, qt.IsNil) 137 138 // Request the exported, remote onion server 139 c.Run("request exported service as onion", func(c *qt.C) { 140 clientCtx, cancel := context.WithTimeout(ctx, clientTimeout) 141 c.Cleanup(cancel) 142 clientDialer, err := torSvc.Dialer(clientCtx, nil) 143 c.Assert(err, qt.IsNil) 144 client := &http.Client{Transport: &http.Transport{DialContext: clientDialer.DialContext}} 145 146 resp, err := ctxhttp.Get(clientCtx, client, "http://"+onionIDs[""]+".onion") 147 c.Assert(err, qt.IsNil) 148 defer resp.Body.Close() 149 respBody, err := ioutil.ReadAll(resp.Body) 150 c.Assert(err, qt.IsNil) 151 152 c.Assert(string(respBody), qt.Equals, "hello world") 153 }) 154 155 // Request an exported unix socket through remote onion server 156 c.Run("request exported unix socket as onion", func(c *qt.C) { 157 clientCtx, cancel := context.WithTimeout(ctx, clientTimeout) 158 c.Cleanup(cancel) 159 clientDialer, err := torSvc.Dialer(clientCtx, nil) 160 c.Assert(err, qt.IsNil) 161 client := &http.Client{Transport: &http.Transport{DialContext: clientDialer.DialContext}} 162 163 resp, err := ctxhttp.Get(clientCtx, client, "http://"+onionIDs[""]+".onion:81") 164 c.Assert(err, qt.IsNil) 165 defer resp.Body.Close() 166 respBody, err := ioutil.ReadAll(resp.Body) 167 c.Assert(err, qt.IsNil) 168 169 c.Assert(string(respBody), qt.Equals, "hello world") 170 }) 171 172 c.Run("request imported onion as local service", func(c *qt.C) { 173 clientCtx, cancel := context.WithTimeout(ctx, clientTimeout) 174 c.Cleanup(cancel) 175 req, err := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:%d/index.html", importPort), nil) 176 c.Assert(err, qt.IsNil) 177 resp, err := ctxhttp.Do(clientCtx, http.DefaultClient, req) 178 c.Assert(err, qt.IsNil) 179 defer resp.Body.Close() 180 respBody, err := ioutil.ReadAll(resp.Body) 181 c.Assert(err, qt.IsNil) 182 183 c.Assert(string(respBody), qt.Contains, "<HTML>") 184 c.Assert(string(respBody), qt.Contains, "Tor Project") 185 }) 186 }