mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
94 lines
2.3 KiB
Go
94 lines
2.3 KiB
Go
package devtunnel
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
frpclient "github.com/fatedier/frp/client"
|
|
frpconfig "github.com/fatedier/frp/pkg/config"
|
|
frpconsts "github.com/fatedier/frp/pkg/consts"
|
|
frplog "github.com/fatedier/frp/pkg/util/log"
|
|
frpcrypto "github.com/fatedier/golib/crypto"
|
|
"github.com/google/uuid"
|
|
"github.com/moby/moby/pkg/namesgenerator"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// New creates a new tunnel pointing at the URL provided. Once created, it
|
|
// returns the external hostname that will resolve to it.
|
|
//
|
|
// The tunnel will exit when the context provided is canceled.
|
|
//
|
|
// Upstream connection occurs synchronously through a selfhosted
|
|
// https://github.com/fatedier/frp instance. The error channel sends an error
|
|
// when the frp client stops.
|
|
func New(ctx context.Context, coderurl *url.URL) (string, <-chan error, error) {
|
|
frpcrypto.DefaultSalt = "frp"
|
|
|
|
cfg := frpconfig.GetDefaultClientConf()
|
|
cfg.ServerAddr = "frp-tunnel.coder.app"
|
|
cfg.ServerPort = 7000
|
|
|
|
// Ignore all logs from frp.
|
|
frplog.InitLog("file", os.DevNull, "error", -1, false)
|
|
|
|
var (
|
|
id = uuid.NewString()
|
|
subdomain = strings.ReplaceAll(namesgenerator.GetRandomName(1), "_", "-")
|
|
portStr = coderurl.Port()
|
|
)
|
|
if portStr == "" {
|
|
portStr = "80"
|
|
}
|
|
|
|
port, err := strconv.ParseInt(portStr, 10, 64)
|
|
if err != nil {
|
|
return "", nil, xerrors.Errorf("parse port %q: %w", port, err)
|
|
}
|
|
|
|
httpcfg := map[string]frpconfig.ProxyConf{
|
|
id: &frpconfig.HTTPProxyConf{
|
|
BaseProxyConf: frpconfig.BaseProxyConf{
|
|
ProxyName: id,
|
|
ProxyType: frpconsts.HTTPProxy,
|
|
UseEncryption: false,
|
|
UseCompression: false,
|
|
LocalSvrConf: frpconfig.LocalSvrConf{
|
|
LocalIP: coderurl.Hostname(),
|
|
LocalPort: int(port),
|
|
},
|
|
},
|
|
DomainConf: frpconfig.DomainConf{
|
|
SubDomain: subdomain,
|
|
},
|
|
Locations: []string{""},
|
|
},
|
|
}
|
|
|
|
if err := httpcfg[id].CheckForCli(); err != nil {
|
|
return "", nil, xerrors.Errorf("check for cli: %w", err)
|
|
}
|
|
|
|
svc, err := frpclient.NewService(cfg, httpcfg, nil, "")
|
|
if err != nil {
|
|
return "", nil, xerrors.Errorf("create new proxy service: %w", err)
|
|
}
|
|
|
|
ch := make(chan error, 1)
|
|
go func() {
|
|
err := svc.Run()
|
|
ch <- err
|
|
close(ch)
|
|
}()
|
|
go func() {
|
|
<-ctx.Done()
|
|
svc.Close()
|
|
}()
|
|
|
|
return fmt.Sprintf("https://%s.try.coder.app", subdomain), ch, nil
|
|
}
|