initial
Browse files- go.mod +5 -0
- go.sum +2 -0
- main.go +54 -0
- src/gotunnelme/get_assigned_url.go +45 -0
- src/gotunnelme/get_assigned_url_test.go +15 -0
- src/gotunnelme/tunnel.go +235 -0
- src/gotunnelme/tunnel_test.go +25 -0
go.mod
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module gotunnelme/go_localt
|
| 2 |
+
|
| 3 |
+
go 1.22.1
|
| 4 |
+
|
| 5 |
+
require github.com/NoahShen/gotunnelme v0.0.0-20180106044115-fbc9b0b77df8 // indirect
|
go.sum
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
github.com/NoahShen/gotunnelme v0.0.0-20180106044115-fbc9b0b77df8 h1:9PnWTGpbxbF4rwTL7q+5ozPyG/pr1NfXXaFTmLXkb5s=
|
| 2 |
+
github.com/NoahShen/gotunnelme v0.0.0-20180106044115-fbc9b0b77df8/go.mod h1:4/5P3Xn6TPHLWG1iHNwvob7WPT3+kr8wBcW/xueIqe4=
|
main.go
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package main
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"github.com/NoahShen/gotunnelme/src/gotunnelme"
|
| 6 |
+
"io/ioutil"
|
| 7 |
+
"net/http"
|
| 8 |
+
"os"
|
| 9 |
+
"strconv"
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
func getExternalIP() (string, error) {
|
| 13 |
+
resp, err := http.Get("http://ipinfo.io/ip")
|
| 14 |
+
if err != nil {
|
| 15 |
+
return "", err
|
| 16 |
+
}
|
| 17 |
+
defer resp.Body.Close()
|
| 18 |
+
ip, err := ioutil.ReadAll(resp.Body)
|
| 19 |
+
if err != nil {
|
| 20 |
+
return "", err
|
| 21 |
+
}
|
| 22 |
+
return string(ip), nil
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
func main() {
|
| 26 |
+
if len(os.Args) == 1 {
|
| 27 |
+
fmt.Fprintln(os.Stderr, "Usage: go_localt <local port>")
|
| 28 |
+
os.Exit(1)
|
| 29 |
+
}
|
| 30 |
+
localPort, err := strconv.Atoi(os.Args[1])
|
| 31 |
+
if err != nil {
|
| 32 |
+
fmt.Fprintln(os.Stderr, err)
|
| 33 |
+
os.Exit(1)
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
t := gotunnelme.NewTunnel()
|
| 37 |
+
url, err := t.GetUrl("")
|
| 38 |
+
if err != nil {
|
| 39 |
+
panic(err)
|
| 40 |
+
}
|
| 41 |
+
fmt.Println(url)
|
| 42 |
+
|
| 43 |
+
extIP, err := getExternalIP()
|
| 44 |
+
if err != nil {
|
| 45 |
+
panic(err)
|
| 46 |
+
}
|
| 47 |
+
fmt.Println(extIP)
|
| 48 |
+
|
| 49 |
+
err = t.CreateTunnel(localPort)
|
| 50 |
+
if err != nil {
|
| 51 |
+
panic(err)
|
| 52 |
+
}
|
| 53 |
+
t.StopTunnel()
|
| 54 |
+
}
|
src/gotunnelme/get_assigned_url.go
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package gotunnelme
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"encoding/json"
|
| 5 |
+
"fmt"
|
| 6 |
+
"io/ioutil"
|
| 7 |
+
"net/http"
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
const (
|
| 11 |
+
localtunnelServer = "http://localtunnel.me/"
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
type AssignedUrlInfo struct {
|
| 15 |
+
Id string `json:"id,omitempty"`
|
| 16 |
+
Url string `json:"url,omitempty"`
|
| 17 |
+
Port int `json:"port,omitempty"`
|
| 18 |
+
MaxConnCount int `json:"max_conn_count,omitempty"`
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
func GetAssignedUrl(assignedDomain string) (*AssignedUrlInfo, error) {
|
| 22 |
+
if len(assignedDomain) == 0 {
|
| 23 |
+
assignedDomain = "?new"
|
| 24 |
+
}
|
| 25 |
+
url := fmt.Sprintf(localtunnelServer+"%s", assignedDomain)
|
| 26 |
+
request, _ := http.NewRequest("GET", url, nil)
|
| 27 |
+
response, httpErr := http.DefaultClient.Do(request)
|
| 28 |
+
if httpErr != nil {
|
| 29 |
+
return nil, httpErr
|
| 30 |
+
}
|
| 31 |
+
defer response.Body.Close()
|
| 32 |
+
bytes, readErr := ioutil.ReadAll(response.Body)
|
| 33 |
+
if readErr != nil {
|
| 34 |
+
return nil, readErr
|
| 35 |
+
}
|
| 36 |
+
if Debug {
|
| 37 |
+
fmt.Printf("***GetAssignedUrl: %s\n", string(bytes))
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
assignedUrlInfo := &AssignedUrlInfo{}
|
| 41 |
+
if unmarshalErr := json.Unmarshal(bytes, assignedUrlInfo); unmarshalErr != nil {
|
| 42 |
+
return nil, unmarshalErr
|
| 43 |
+
}
|
| 44 |
+
return assignedUrlInfo, nil
|
| 45 |
+
}
|
src/gotunnelme/get_assigned_url_test.go
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package gotunnelme
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"testing"
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
func _TestGetAssignedUrl(t *testing.T) {
|
| 9 |
+
Debug = false
|
| 10 |
+
assignedUrlInfo, err := GetAssignedUrl("noah")
|
| 11 |
+
if err != nil {
|
| 12 |
+
t.Fatal(err)
|
| 13 |
+
}
|
| 14 |
+
fmt.Printf("assignedUrlInfo: %+v\n", assignedUrlInfo)
|
| 15 |
+
}
|
src/gotunnelme/tunnel.go
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package gotunnelme
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"bufio"
|
| 5 |
+
"errors"
|
| 6 |
+
"fmt"
|
| 7 |
+
"io"
|
| 8 |
+
"net"
|
| 9 |
+
"net/http"
|
| 10 |
+
"net/url"
|
| 11 |
+
"os"
|
| 12 |
+
"strings"
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
var Debug = false
|
| 16 |
+
|
| 17 |
+
type TunnelConn struct {
|
| 18 |
+
remoteHost string
|
| 19 |
+
remotePort int
|
| 20 |
+
localPort int
|
| 21 |
+
remoteConn net.Conn
|
| 22 |
+
localConn net.Conn
|
| 23 |
+
errorChannel chan error
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
func NewTunnelConn(remoteHost string, remotePort, localPort int) *TunnelConn {
|
| 27 |
+
tunnelConn := &TunnelConn{}
|
| 28 |
+
tunnelConn.remoteHost = remoteHost
|
| 29 |
+
tunnelConn.remotePort = remotePort
|
| 30 |
+
tunnelConn.localPort = localPort
|
| 31 |
+
return tunnelConn
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
func (self *TunnelConn) Tunnel(replyCh chan<- int) error {
|
| 35 |
+
self.errorChannel = make(chan error, 1) // clear previous channel's message
|
| 36 |
+
remoteConn, remoteErr := self.connectRemote()
|
| 37 |
+
if remoteErr != nil {
|
| 38 |
+
if Debug {
|
| 39 |
+
fmt.Printf("Connect remote error[%s]!\n", remoteErr.Error())
|
| 40 |
+
}
|
| 41 |
+
replyCh <- -1
|
| 42 |
+
return remoteErr
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
if Debug {
|
| 46 |
+
fmt.Printf("Connect remote[%s:%d] successful!\n", self.remoteHost, self.remotePort)
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
localConn, localErr := self.connectLocal()
|
| 50 |
+
if localErr != nil {
|
| 51 |
+
if Debug {
|
| 52 |
+
fmt.Printf("Connect local error[%s]!\n", localErr.Error())
|
| 53 |
+
}
|
| 54 |
+
replyCh <- -1
|
| 55 |
+
return localErr
|
| 56 |
+
}
|
| 57 |
+
if Debug {
|
| 58 |
+
fmt.Printf("Connect local[:%d] successful!\n", self.localPort)
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
self.remoteConn = remoteConn
|
| 62 |
+
self.localConn = localConn
|
| 63 |
+
go func() {
|
| 64 |
+
var err error
|
| 65 |
+
for {
|
| 66 |
+
_, err = io.Copy(remoteConn, localConn)
|
| 67 |
+
if err != nil {
|
| 68 |
+
if Debug {
|
| 69 |
+
fmt.Printf("Stop copy form local to remote! error=[%v]\n", err)
|
| 70 |
+
}
|
| 71 |
+
break
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
self.errorChannel <- err
|
| 75 |
+
}()
|
| 76 |
+
go func() {
|
| 77 |
+
var err error
|
| 78 |
+
for {
|
| 79 |
+
_, err = io.Copy(localConn, remoteConn)
|
| 80 |
+
if err != nil {
|
| 81 |
+
if Debug {
|
| 82 |
+
fmt.Printf("Stop copy form remote to local! error=[%v]\n", err)
|
| 83 |
+
}
|
| 84 |
+
break
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
}
|
| 88 |
+
self.errorChannel <- err
|
| 89 |
+
}()
|
| 90 |
+
err := <-self.errorChannel
|
| 91 |
+
replyCh <- 1
|
| 92 |
+
return err
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
func (self *TunnelConn) StopTunnel() error {
|
| 96 |
+
if self.remoteConn != nil {
|
| 97 |
+
self.remoteConn.Close()
|
| 98 |
+
}
|
| 99 |
+
if self.localConn != nil {
|
| 100 |
+
self.localConn.Close()
|
| 101 |
+
}
|
| 102 |
+
return nil
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
func (self *TunnelConn) connectRemote() (net.Conn, error) {
|
| 106 |
+
remoteAddr := fmt.Sprintf("%s:%d", self.remoteHost, self.remotePort)
|
| 107 |
+
addr := remoteAddr
|
| 108 |
+
proxy := os.Getenv("HTTP_PROXY")
|
| 109 |
+
if proxy == "" {
|
| 110 |
+
proxy = os.Getenv("http_proxy")
|
| 111 |
+
}
|
| 112 |
+
if len(proxy) > 0 {
|
| 113 |
+
url, err := url.Parse(proxy)
|
| 114 |
+
if err == nil {
|
| 115 |
+
addr = url.Host
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
remoteConn, remoteErr := net.Dial("tcp", addr)
|
| 119 |
+
if remoteErr != nil {
|
| 120 |
+
return nil, remoteErr
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
if len(proxy) > 0 {
|
| 124 |
+
fmt.Fprintf(remoteConn, "CONNECT %s HTTP/1.1\r\n", remoteAddr)
|
| 125 |
+
fmt.Fprintf(remoteConn, "Host: %s\r\n", remoteAddr)
|
| 126 |
+
fmt.Fprintf(remoteConn, "\r\n")
|
| 127 |
+
br := bufio.NewReader(remoteConn)
|
| 128 |
+
req, _ := http.NewRequest("CONNECT", remoteAddr, nil)
|
| 129 |
+
resp, readRespErr := http.ReadResponse(br, req)
|
| 130 |
+
if readRespErr != nil {
|
| 131 |
+
return nil, readRespErr
|
| 132 |
+
}
|
| 133 |
+
if resp.StatusCode != 200 {
|
| 134 |
+
f := strings.SplitN(resp.Status, " ", 2)
|
| 135 |
+
return nil, errors.New(f[1])
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
if Debug {
|
| 139 |
+
fmt.Printf("Connect %s by proxy[%s].\n", remoteAddr, proxy)
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
return remoteConn, nil
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
func (self *TunnelConn) connectLocal() (net.Conn, error) {
|
| 146 |
+
localAddr := fmt.Sprintf("%s:%d", "localhost", self.localPort)
|
| 147 |
+
return net.Dial("tcp", localAddr)
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
type TunnelCommand int
|
| 151 |
+
|
| 152 |
+
const (
|
| 153 |
+
stopTunnelCmd TunnelCommand = 1
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
type Tunnel struct {
|
| 157 |
+
assignedUrlInfo *AssignedUrlInfo
|
| 158 |
+
localPort int
|
| 159 |
+
tunnelConns []*TunnelConn
|
| 160 |
+
cmdChan chan TunnelCommand
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
func NewTunnel() *Tunnel {
|
| 164 |
+
tunnel := &Tunnel{}
|
| 165 |
+
tunnel.cmdChan = make(chan TunnelCommand, 1)
|
| 166 |
+
return tunnel
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
func (self *Tunnel) startTunnel() error {
|
| 170 |
+
if err := self.checkLocalPort(); err != nil {
|
| 171 |
+
return err
|
| 172 |
+
}
|
| 173 |
+
url, parseErr := url.Parse(localtunnelServer)
|
| 174 |
+
if parseErr != nil {
|
| 175 |
+
return parseErr
|
| 176 |
+
}
|
| 177 |
+
replyCh := make(chan int, self.assignedUrlInfo.MaxConnCount)
|
| 178 |
+
remoteHost := url.Host
|
| 179 |
+
for i := 0; i < self.assignedUrlInfo.MaxConnCount; i++ {
|
| 180 |
+
tunnelConn := NewTunnelConn(remoteHost, self.assignedUrlInfo.Port, self.localPort)
|
| 181 |
+
self.tunnelConns[i] = tunnelConn
|
| 182 |
+
go tunnelConn.Tunnel(replyCh)
|
| 183 |
+
}
|
| 184 |
+
L:
|
| 185 |
+
for i := 0; i < self.assignedUrlInfo.MaxConnCount; i++ {
|
| 186 |
+
select {
|
| 187 |
+
case <-replyCh:
|
| 188 |
+
case cmd := <-self.cmdChan:
|
| 189 |
+
switch cmd {
|
| 190 |
+
case stopTunnelCmd:
|
| 191 |
+
break L
|
| 192 |
+
}
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
return nil
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
func (self *Tunnel) checkLocalPort() error {
|
| 200 |
+
localAddr := fmt.Sprintf("%s:%d", "localhost", self.localPort)
|
| 201 |
+
c, err := net.Dial("tcp", localAddr)
|
| 202 |
+
if err != nil {
|
| 203 |
+
return errors.New("can't connect local port!")
|
| 204 |
+
}
|
| 205 |
+
c.Close()
|
| 206 |
+
return nil
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
func (self *Tunnel) StopTunnel() {
|
| 210 |
+
if Debug {
|
| 211 |
+
fmt.Printf("Stop tunnel for localPort[%d]!\n", self.localPort)
|
| 212 |
+
}
|
| 213 |
+
self.cmdChan <- stopTunnelCmd
|
| 214 |
+
for _, tunnelCon := range self.tunnelConns {
|
| 215 |
+
tunnelCon.StopTunnel()
|
| 216 |
+
}
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
func (self *Tunnel) GetUrl(assignedDomain string) (string, error) {
|
| 220 |
+
if len(assignedDomain) == 0 {
|
| 221 |
+
assignedDomain = "?new"
|
| 222 |
+
}
|
| 223 |
+
assignedUrlInfo, err := GetAssignedUrl(assignedDomain)
|
| 224 |
+
if err != nil {
|
| 225 |
+
return "", err
|
| 226 |
+
}
|
| 227 |
+
self.assignedUrlInfo = assignedUrlInfo
|
| 228 |
+
self.tunnelConns = make([]*TunnelConn, assignedUrlInfo.MaxConnCount)
|
| 229 |
+
return assignedUrlInfo.Url, nil
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
func (self *Tunnel) CreateTunnel(localPort int) error {
|
| 233 |
+
self.localPort = localPort
|
| 234 |
+
return self.startTunnel()
|
| 235 |
+
}
|
src/gotunnelme/tunnel_test.go
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package gotunnelme
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"fmt"
|
| 5 |
+
"testing"
|
| 6 |
+
"time"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
func TestTunnel(t *testing.T) {
|
| 10 |
+
Debug = true
|
| 11 |
+
tunnel := NewTunnel()
|
| 12 |
+
url, getUrlErr := tunnel.GetUrl("noah")
|
| 13 |
+
if getUrlErr != nil {
|
| 14 |
+
t.Fatal(getUrlErr)
|
| 15 |
+
}
|
| 16 |
+
fmt.Println("Get Url:", url)
|
| 17 |
+
go func() {
|
| 18 |
+
tunnelErr := tunnel.CreateTunnel(8787)
|
| 19 |
+
if tunnelErr != nil {
|
| 20 |
+
t.Fatal(tunnelErr)
|
| 21 |
+
}
|
| 22 |
+
}()
|
| 23 |
+
time.Sleep(30 * time.Second)
|
| 24 |
+
tunnel.StopTunnel()
|
| 25 |
+
}
|