/
tunnel.go
127 lines (104 loc) · 3.11 KB
/
tunnel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package ngrok
import (
"context"
"net"
"time"
"github.com/ngrok/ngrok-go/config"
tunnel_client "github.com/ngrok/ngrok-go/internal/tunnel/client"
)
// An ngrok tunnel.
type Tunnel interface {
// Every Tunnel is a net.Listener. It can be plugged into any existing
// code that expects a net.Listener seamlessly without any changes.
net.Listener
// Returns the ForwardsTo string for this tunnel.
ForwardsTo() string
// Returns the Metadata string for this tunnel.
Metadata() string
// Returns this tunnel's ID.
ID() string
// Returns this tunnel's protocol.
// Will be empty for labeled tunnels.
Proto() string
// Returns the URL for this tunnel.
// Will be empty for labeled tunnels.
URL() string
// Returns the labels for this tunnel.
// Will be empty for non-labeled tunnels.
Labels() map[string]string
// Session returns the tunnel's parent Session object that it
// was started on.
Session() Session
// Convenience method that calls `CloseWithContext` with a default timeout
// of 5 seconds.
Close() error
// Closing a tunnel is an operation that involves sending a "close" message
// over the existing session. Since this is subject to network latency,
// packet loss, etc., it is most correct to provide a context. See also
// `Close`, which matches the `io.Closer` interface method.
CloseWithContext(context.Context) error
}
// Create a new ngrok session and start a tunnel.
// Shorthand for a [Connect] followed by a [Session].StartTunnel.
// If an error is encoutered when starting the tunnel, but after a session has
// been established, both the [Session] and error return values will be non-nil.
func StartTunnel(ctx context.Context, tunnelConfig config.Tunnel, connectOpts ...ConnectOption) (Tunnel, error) {
sess, err := Connect(ctx, connectOpts...)
if err != nil {
return nil, err
}
return sess.StartTunnel(ctx, tunnelConfig)
}
type tunnelImpl struct {
Sess Session
Tunnel tunnel_client.Tunnel
}
func (t *tunnelImpl) Accept() (net.Conn, error) {
conn, err := t.Tunnel.Accept()
if err != nil {
return nil, errAcceptFailed{Inner: err}
}
return &connImpl{
Conn: conn.Conn,
Proxy: conn,
}, nil
}
func (t *tunnelImpl) Close() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
return t.CloseWithContext(ctx)
}
func (t *tunnelImpl) CloseWithContext(_ context.Context) error {
return t.Tunnel.Close()
}
func (t *tunnelImpl) Addr() net.Addr {
return t.Tunnel.Addr()
}
func (t *tunnelImpl) URL() string {
return t.Tunnel.RemoteBindConfig().URL
}
func (t *tunnelImpl) Proto() string {
return t.Tunnel.RemoteBindConfig().ConfigProto
}
func (t *tunnelImpl) ForwardsTo() string {
return t.Tunnel.ForwardsTo()
}
func (t *tunnelImpl) Metadata() string {
return t.Tunnel.RemoteBindConfig().Metadata
}
func (t *tunnelImpl) ID() string {
return t.Tunnel.ID()
}
func (t *tunnelImpl) Labels() map[string]string {
return t.Tunnel.RemoteBindConfig().Labels
}
func (t *tunnelImpl) Session() Session {
return t.Sess
}
type connImpl struct {
net.Conn
Proxy *tunnel_client.ProxyConn
}
func (c *connImpl) ProxyConn() *tunnel_client.ProxyConn {
return c.Proxy
}