2021-06-22 00:31:24 +00:00
package tunnel
import (
"encoding/json"
"fmt"
2024-06-26 13:17:20 +00:00
"io"
2021-06-22 00:31:24 +00:00
"net/http"
2021-07-28 08:27:05 +00:00
"strings"
2021-06-22 00:31:24 +00:00
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/cloudflare/cloudflared/connection"
)
const httpTimeout = 15 * time . Second
2024-09-06 18:33:42 +00:00
const disclaimer = "Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee, are subject to the Cloudflare Online Services Terms of Use (https://www.cloudflare.com/website-terms/), and Cloudflare reserves the right to investigate your use of Tunnels for violations of such terms. If you intend to use Tunnels in production you should use a pre-created named tunnel by following: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps"
2021-07-09 17:52:41 +00:00
2021-06-22 00:31:24 +00:00
// RunQuickTunnel requests a tunnel from the specified service.
// We use this to power quick tunnels on trycloudflare.com, but the
// service is open-source and could be used by anyone.
func RunQuickTunnel ( sc * subcommandContext ) error {
2021-07-09 17:52:41 +00:00
sc . log . Info ( ) . Msg ( disclaimer )
sc . log . Info ( ) . Msg ( "Requesting new quick Tunnel on trycloudflare.com..." )
2021-06-22 00:31:24 +00:00
client := http . Client {
Transport : & http . Transport {
TLSHandshakeTimeout : httpTimeout ,
ResponseHeaderTimeout : httpTimeout ,
} ,
Timeout : httpTimeout ,
}
2024-06-25 21:15:50 +00:00
req , err := http . NewRequest ( http . MethodPost , fmt . Sprintf ( "%s/tunnel" , sc . c . String ( "quick-service" ) ) , nil )
if err != nil {
return errors . Wrap ( err , "failed to build quick tunnel request" )
}
req . Header . Add ( "Content-Type" , "application/json" )
req . Header . Add ( "User-Agent" , buildInfo . UserAgent ( ) )
resp , err := client . Do ( req )
2021-06-22 00:31:24 +00:00
if err != nil {
2021-07-09 17:52:41 +00:00
return errors . Wrap ( err , "failed to request quick Tunnel" )
2021-06-22 00:31:24 +00:00
}
defer resp . Body . Close ( )
2024-06-26 13:17:20 +00:00
// This will read the entire response into memory so we can print it in case of error
rsp_body , err := io . ReadAll ( resp . Body )
if err != nil {
return errors . Wrap ( err , "failed to read quick-tunnel response" )
}
2021-06-22 00:31:24 +00:00
var data QuickTunnelResponse
2024-06-26 13:17:20 +00:00
if err := json . Unmarshal ( rsp_body , & data ) ; err != nil {
rsp_string := string ( rsp_body )
fields := map [ string ] interface { } { "status_code" : resp . Status }
sc . log . Err ( err ) . Fields ( fields ) . Msgf ( "Error unmarshaling QuickTunnel response: %s" , rsp_string )
2021-07-09 17:52:41 +00:00
return errors . Wrap ( err , "failed to unmarshal quick Tunnel" )
2021-06-22 00:31:24 +00:00
}
tunnelID , err := uuid . Parse ( data . Result . ID )
if err != nil {
2021-07-09 17:52:41 +00:00
return errors . Wrap ( err , "failed to parse quick Tunnel ID" )
2021-06-22 00:31:24 +00:00
}
credentials := connection . Credentials {
AccountTag : data . Result . AccountTag ,
TunnelSecret : data . Result . Secret ,
TunnelID : tunnelID ,
}
2021-07-28 08:27:05 +00:00
url := data . Result . Hostname
if ! strings . HasPrefix ( url , "https://" ) {
url = "https://" + url
}
2021-07-09 17:52:41 +00:00
for _ , line := range AsciiBox ( [ ] string {
"Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):" ,
2021-07-28 08:27:05 +00:00
url ,
2021-06-22 00:31:24 +00:00
} , 2 ) {
sc . log . Info ( ) . Msg ( line )
}
2021-10-12 17:25:38 +00:00
if ! sc . c . IsSet ( "protocol" ) {
sc . c . Set ( "protocol" , "quic" )
}
2023-03-13 10:20:58 +00:00
// Override the number of connections used. Quick tunnels shouldn't be used for production usage,
// so, use a single connection instead.
sc . c . Set ( haConnectionsFlag , "1" )
2021-06-22 00:31:24 +00:00
return StartServer (
sc . c ,
2021-12-27 19:05:14 +00:00
buildInfo ,
2024-05-24 18:40:10 +00:00
& connection . TunnelProperties { Credentials : credentials , QuickTunnelUrl : data . Result . Hostname } ,
2021-06-22 00:31:24 +00:00
sc . log ,
)
}
type QuickTunnelResponse struct {
Success bool
Result QuickTunnel
Errors [ ] QuickTunnelError
}
type QuickTunnelError struct {
Code int
Message string
}
type QuickTunnel struct {
ID string ` json:"id" `
Name string ` json:"name" `
Hostname string ` json:"hostname" `
AccountTag string ` json:"account_tag" `
Secret [ ] byte ` json:"secret" `
}
2021-07-09 17:52:41 +00:00
// Print out the given lines in a nice ASCII box.
func AsciiBox ( lines [ ] string , padding int ) ( box [ ] string ) {
maxLen := maxLen ( lines )
spacer := strings . Repeat ( " " , padding )
border := "+" + strings . Repeat ( "-" , maxLen + ( padding * 2 ) ) + "+"
box = append ( box , border )
for _ , line := range lines {
box = append ( box , "|" + spacer + line + strings . Repeat ( " " , maxLen - len ( line ) ) + spacer + "|" )
}
box = append ( box , border )
return
}
func maxLen ( lines [ ] string ) int {
max := 0
for _ , line := range lines {
if len ( line ) > max {
max = len ( line )
}
}
return max
}