mirror of
https://github.com/cloudflare/cloudflared
synced 2026-06-08 13:33:07 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f4407ce27 | |||
| d9636c73b4 | |||
| 6cbf90883d | |||
| 76ade67f95 | |||
| 997f2cf612 | |||
| ceb509ee98 | |||
| 5a3c0fdffa | |||
| 2822fbe3db | |||
| 5148d00516 | |||
| bb10e1dee5 | |||
| e445fd92f7 | |||
| bccf4a63dc | |||
| 7059ef8e13 | |||
| cbdf88ea28 | |||
| 79ebfa8304 |
Vendored
+3
-3
@@ -46,8 +46,8 @@ git reset --hard origin/master
|
||||
URL="https://packages.argotunnel.com/dl/cloudflared-$VERSION-darwin-amd64.tgz"
|
||||
tee cloudflared.rb <<EOF
|
||||
class Cloudflared < Formula
|
||||
desc 'Argo Tunnel'
|
||||
homepage 'https://developers.cloudflare.com/argo-tunnel/'
|
||||
desc 'Cloudflare Tunnel'
|
||||
homepage 'https://developers.cloudflare.com/cloudflare-one/connections/connect-apps'
|
||||
url '$URL'
|
||||
sha256 '$SHA256'
|
||||
version '$VERSION'
|
||||
@@ -62,6 +62,6 @@ git add cloudflared.rb
|
||||
git diff
|
||||
git config user.name "cloudflare-warp-bot"
|
||||
git config user.email "warp-bot@cloudflare.com"
|
||||
git commit -m "Release Argo Tunnel $VERSION"
|
||||
git commit -m "Release Cloudflare Tunnel $VERSION"
|
||||
|
||||
git push -v origin master
|
||||
|
||||
+13
@@ -1,5 +1,18 @@
|
||||
**Experimental**: This is a new format for release notes. The format and availability is subject to change.
|
||||
|
||||
## 2021.9.2
|
||||
|
||||
### New features
|
||||
- `cloudflared` can now run with `quic` as the underlying tunnel transport protocol. To try it, change or add "protocol: quic" to your config.yml file or
|
||||
run cloudflared with the `--protocol quic` flag. e.g:
|
||||
`cloudflared tunnel --protocol quic run <tunnel-name>`
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed some generic transport bugs in `quic` mode. It's advised to upgrade to at least this version (2021.9.2) when running `cloudflared`
|
||||
with `quic` protocol.
|
||||
- `cloudflared` docker images will now show version.
|
||||
|
||||
|
||||
## 2021.8.4
|
||||
### Improvements
|
||||
- Temporary tunnels (those hosted on trycloudflare.com that do not require a Cloudflare login) now run as Named Tunnels
|
||||
|
||||
@@ -132,7 +132,7 @@ define build_package
|
||||
cp cloudflared $(PACKAGE_DIR)/cloudflared
|
||||
cat cloudflared_man_template | sed -e 's/\$${VERSION}/$(VERSION)/; s/\$${DATE}/$(DATE)/' > $(PACKAGE_DIR)/cloudflared.1
|
||||
fakeroot fpm -C $(PACKAGE_DIR) -s dir -t $(1) \
|
||||
--description 'Cloudflare Argo tunnel daemon' \
|
||||
--description 'Cloudflare Tunnel daemon' \
|
||||
--vendor 'Cloudflare' \
|
||||
--license 'Cloudflare Service Agreement' \
|
||||
--url 'https://github.com/cloudflare/cloudflared' \
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
# Argo Tunnel client
|
||||
# Cloudflare Tunnel client
|
||||
|
||||
Contains the command-line client for Cloudflare Tunnel, a tunneling daemon that proxies traffic from the Cloudflare network to your origins.
|
||||
This daemon sits between Cloudflare network and your origin (e.g. a webserver). Cloudflare attracts client requests and sends them to you
|
||||
via this daemon, without requiring you to poke holes on your firewall --- your origin can remain as closed as possible.
|
||||
Extensive documentation can be found in the [Cloudflare Tunnel section](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) of the Cloudflare Docs.
|
||||
All usages related with proxying to your origins are available under `cloudflared tunnel help`.
|
||||
|
||||
You can also use `cloudflared` to access Tunnel origins (that are protected with `cloudflared tunnel`) for TCP traffic
|
||||
at Layer 4 (i.e., not HTTP/websocket), which is relevant for use cases such as SSH, RDP, etc.
|
||||
Such usages are available under `cloudflared access help`.
|
||||
|
||||
You can instead use [WARP client](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/private-networks)
|
||||
to access private origins behind Tunnels for Layer 4 traffic without requiring `cloudflared access` commands on the client side.
|
||||
|
||||
Contains the command-line client for Argo Tunnel, a tunneling daemon that proxies any local webserver through the Cloudflare network. Extensive documentation can be found in the [Argo Tunnel section](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) of the Cloudflare Docs.
|
||||
|
||||
## Before you get started
|
||||
|
||||
Before you use Argo Tunnel, you'll need to complete a few steps in the Cloudflare dashboard. The website you add to Cloudflare will be used to route traffic to your Tunnel.
|
||||
|
||||
Before you use Cloudflare Tunnel, you'll need to complete a few steps in the Cloudflare dashboard: you need to add a
|
||||
website to your Cloudflare account. Note that today it is possible to use Tunnel without a website (e.g. for private
|
||||
routing), but for legacy reasons this requirement is still necessary:
|
||||
1. [Add a website to Cloudflare](https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website)
|
||||
2. [Change your domain nameservers to Cloudflare](https://support.cloudflare.com/hc/en-us/articles/205195708)
|
||||
|
||||
|
||||
## Installing `cloudflared`
|
||||
|
||||
Downloads are available as standalone binaries, a Docker image, and Debian, RPM, and Homebrew packages. You can also find releases here on the `cloudflared` GitHub repository.
|
||||
@@ -18,18 +32,23 @@ Downloads are available as standalone binaries, a Docker image, and Debian, RPM,
|
||||
* A Docker image of `cloudflared` is [available on DockerHub](https://hub.docker.com/r/cloudflare/cloudflared)
|
||||
* You can install on Windows machines with the [steps here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#windows)
|
||||
|
||||
User documentation for Argo Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
|
||||
User documentation for Cloudflare Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/connections/connect-apps
|
||||
|
||||
|
||||
## Creating Tunnels and routing traffic
|
||||
|
||||
Once installed, you can authenticate `cloudflared` into your Cloudflare account and begin creating Tunnels that serve traffic for hostnames in your account.
|
||||
Once installed, you can authenticate `cloudflared` into your Cloudflare account and begin creating Tunnels to serve traffic to your origins.
|
||||
|
||||
* Create a Tunnel with [these instructions](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/create-tunnel)
|
||||
* Route traffic to that Tunnel with [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns) or with a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/lb)
|
||||
* Route traffic to that Tunnel:
|
||||
* Via public [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns)
|
||||
* Or via a public hostname guided by a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/lb)
|
||||
* Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/private-networks)
|
||||
|
||||
|
||||
## TryCloudflare
|
||||
|
||||
Want to test Argo Tunnel before adding a website to Cloudflare? You can do so with TryCloudflare using the documentation [available here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/trycloudflare).
|
||||
Want to test Cloudflare Tunnel before adding a website to Cloudflare? You can do so with TryCloudflare using the documentation [available here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/trycloudflare).
|
||||
|
||||
## Deprecated versions
|
||||
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
2021.10.3
|
||||
- 2021-10-14 TUN-5255: Fix potential panic if Cloudflare API fails to respond to GetTunnel(id) during delete command
|
||||
- 2021-10-14 TUN-5257: Fix more cfsetup targets that were broken by recent package changes
|
||||
|
||||
2021.10.2
|
||||
- 2021-10-11 TUN-5138: Switch to QUIC on auto protocol based on threshold
|
||||
- 2021-10-14 TUN-5250: Add missing packages for cfsetup to succeed in github release pkgs target
|
||||
|
||||
2021.10.1
|
||||
- 2021-10-12 TUN-5246: Use protocol: quic for Quick tunnels if one is not already set
|
||||
- 2021-10-13 TUN-5249: Revert "TUN-5138: Switch to QUIC on auto protocol based on threshold"
|
||||
|
||||
2021.10.0
|
||||
- 2021-10-11 TUN-5138: Switch to QUIC on auto protocol based on threshold
|
||||
- 2021-10-07 TUN-5195: Do not set empty body if not applicable
|
||||
- 2021-10-08 UN-5213: Increase MaxStreams value for QUIC transport
|
||||
- 2021-09-28 TUN-5169: Release 2021.9.2 CHANGES.md
|
||||
- 2021-09-28 TUN-5164: Update README and clean up references to Argo Tunnel (using Cloudflare Tunnel instead)
|
||||
|
||||
2021.9.2
|
||||
- 2021-09-21 TUN-5129: Use go 1.17 and copy .git folder to docker build to compute version
|
||||
- 2021-09-21 TUN-5128: Enforce maximum grace period
|
||||
|
||||
@@ -55,6 +55,8 @@ stretch: &stretch
|
||||
# libmsi and libgcab are libraries the wixl binary depends on.
|
||||
- libmsi-dev
|
||||
- libgcab-dev
|
||||
- python3-dev
|
||||
- libffi-dev
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
pre-cache:
|
||||
@@ -111,6 +113,9 @@ stretch: &stretch
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- build-essential
|
||||
- python3-dev
|
||||
- libffi-dev
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
pre-cache: &install_pygithub
|
||||
@@ -163,6 +168,9 @@ stretch: &stretch
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- build-essential
|
||||
- python3-dev
|
||||
- libffi-dev
|
||||
- python3-setuptools
|
||||
- python3-pip
|
||||
pre-cache: *install_pygithub
|
||||
|
||||
@@ -11,7 +11,7 @@ func RemovedCommand(name string) *cli.Command {
|
||||
Name: name,
|
||||
Action: func(context *cli.Context) error {
|
||||
return cli.Exit(
|
||||
fmt.Sprintf("%s command is no longer supported by cloudflared. Consult Argo Tunnel documentation for possible alternative solutions.", name),
|
||||
fmt.Sprintf("%s command is no longer supported by cloudflared. Consult Cloudflare Tunnel documentation for possible alternative solutions.", name),
|
||||
-1,
|
||||
)
|
||||
},
|
||||
|
||||
@@ -19,11 +19,11 @@ import (
|
||||
func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
app.Commands = append(app.Commands, &cli.Command{
|
||||
Name: "service",
|
||||
Usage: "Manages the Argo Tunnel system service",
|
||||
Usage: "Manages the Cloudflare Tunnel system service",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install Argo Tunnel as a system service",
|
||||
Usage: "Install Cloudflare Tunnel as a system service",
|
||||
Action: cliutil.ConfiguredAction(installLinuxService),
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
@@ -34,7 +34,7 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
},
|
||||
{
|
||||
Name: "uninstall",
|
||||
Usage: "Uninstall the Argo Tunnel service",
|
||||
Usage: "Uninstall the Cloudflare Tunnel service",
|
||||
Action: cliutil.ConfiguredAction(uninstallLinuxService),
|
||||
},
|
||||
},
|
||||
@@ -55,7 +55,7 @@ var systemdTemplates = []ServiceTemplate{
|
||||
{
|
||||
Path: "/etc/systemd/system/cloudflared.service",
|
||||
Content: `[Unit]
|
||||
Description=Argo Tunnel
|
||||
Description=Cloudflare Tunnel
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
@@ -72,7 +72,7 @@ WantedBy=multi-user.target
|
||||
{
|
||||
Path: "/etc/systemd/system/cloudflared-update.service",
|
||||
Content: `[Unit]
|
||||
Description=Update Argo Tunnel
|
||||
Description=Update Cloudflare Tunnel
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
@@ -82,7 +82,7 @@ ExecStart=/bin/bash -c '{{ .Path }} update; code=$?; if [ $code -eq 11 ]; then s
|
||||
{
|
||||
Path: "/etc/systemd/system/cloudflared-update.timer",
|
||||
Content: `[Unit]
|
||||
Description=Update Argo Tunnel
|
||||
Description=Update Cloudflare Tunnel
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
@@ -99,7 +99,7 @@ var sysvTemplate = ServiceTemplate{
|
||||
Content: `#!/bin/sh
|
||||
# For RedHat and cousins:
|
||||
# chkconfig: 2345 99 01
|
||||
# description: Argo Tunnel agent
|
||||
# description: Cloudflare Tunnel agent
|
||||
# processname: {{.Path}}
|
||||
### BEGIN INIT INFO
|
||||
# Provides: {{.Path}}
|
||||
@@ -107,8 +107,8 @@ var sysvTemplate = ServiceTemplate{
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Argo Tunnel
|
||||
# Description: Argo Tunnel agent
|
||||
# Short-Description: Cloudflare Tunnel
|
||||
# Description: Cloudflare Tunnel agent
|
||||
### END INIT INFO
|
||||
name=$(basename $(readlink -f $0))
|
||||
cmd="{{.Path}} --config /etc/cloudflared/config.yml --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}"
|
||||
|
||||
@@ -20,16 +20,16 @@ const (
|
||||
func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
app.Commands = append(app.Commands, &cli.Command{
|
||||
Name: "service",
|
||||
Usage: "Manages the Argo Tunnel launch agent",
|
||||
Usage: "Manages the Cloudflare Tunnel launch agent",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install Argo Tunnel as an user launch agent",
|
||||
Usage: "Install Cloudflare Tunnel as an user launch agent",
|
||||
Action: cliutil.ConfiguredAction(installLaunchd),
|
||||
},
|
||||
{
|
||||
Name: "uninstall",
|
||||
Usage: "Uninstall the Argo Tunnel launch agent",
|
||||
Usage: "Uninstall the Cloudflare Tunnel launch agent",
|
||||
Action: cliutil.ConfiguredAction(uninstallLaunchd),
|
||||
},
|
||||
},
|
||||
@@ -110,13 +110,13 @@ func installLaunchd(c *cli.Context) error {
|
||||
log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
|
||||
|
||||
if isRootUser() {
|
||||
log.Info().Msg("Installing Argo Tunnel client as a system launch daemon. " +
|
||||
"Argo Tunnel client will run at boot")
|
||||
log.Info().Msg("Installing Cloudflare Tunnel client as a system launch daemon. " +
|
||||
"Cloudflare Tunnel client will run at boot")
|
||||
} else {
|
||||
log.Info().Msg("Installing Argo Tunnel client as an user launch agent. " +
|
||||
"Note that Argo Tunnel client will only run when the user is logged in. " +
|
||||
"If you want to run Argo Tunnel client at boot, install with root permission. " +
|
||||
"For more information, visit https://developers.cloudflare.com/argo-tunnel/reference/service/")
|
||||
log.Info().Msg("Installing Cloudflare Tunnel client as an user launch agent. " +
|
||||
"Note that Cloudflare Tunnel client will only run when the user is logged in. " +
|
||||
"If you want to run Cloudflare Tunnel client at boot, install with root permission. " +
|
||||
"For more information, visit https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/run-as-service")
|
||||
}
|
||||
etPath, err := os.Executable()
|
||||
if err != nil {
|
||||
@@ -159,9 +159,9 @@ func uninstallLaunchd(c *cli.Context) error {
|
||||
log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
|
||||
|
||||
if isRootUser() {
|
||||
log.Info().Msg("Uninstalling Argo Tunnel as a system launch daemon")
|
||||
log.Info().Msg("Uninstalling Cloudflare Tunnel as a system launch daemon")
|
||||
} else {
|
||||
log.Info().Msg("Uninstalling Argo Tunnel as an user launch agent")
|
||||
log.Info().Msg("Uninstalling Cloudflare Tunnel as an user launch agent")
|
||||
}
|
||||
installPath, err := installPath()
|
||||
if err != nil {
|
||||
|
||||
@@ -67,7 +67,7 @@ func main() {
|
||||
app.Copyright = fmt.Sprintf(
|
||||
`(c) %d Cloudflare Inc.
|
||||
Your installation of cloudflared software constitutes a symbol of your signature indicating that you accept
|
||||
the terms of the Cloudflare License (https://developers.cloudflare.com/argo-tunnel/license/),
|
||||
the terms of the Cloudflare License (https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/license),
|
||||
Terms (https://www.cloudflare.com/terms/) and Privacy Policy (https://www.cloudflare.com/privacypolicy/).`,
|
||||
time.Now().Year(),
|
||||
)
|
||||
@@ -76,7 +76,7 @@ func main() {
|
||||
You can use it to authenticate a session to reach an API behind Access, route web traffic to this machine,
|
||||
and configure access control.
|
||||
|
||||
See https://developers.cloudflare.com/argo-tunnel/ for more in-depth documentation.`
|
||||
See https://developers.cloudflare.com/cloudflare-one/connections/connect-apps for more in-depth documentation.`
|
||||
app.Flags = flags()
|
||||
app.Action = action(graceShutdownC)
|
||||
app.Commands = commands(cli.ShowVersion)
|
||||
|
||||
@@ -126,14 +126,14 @@ func buildTunnelCommand(subcommands []*cli.Command) *cli.Command {
|
||||
Name: "tunnel",
|
||||
Action: cliutil.ConfiguredAction(TunnelCommand),
|
||||
Category: "Tunnel",
|
||||
Usage: "Make a locally-running web service accessible over the internet using Argo Tunnel.",
|
||||
Usage: "Make a locally-running web service accessible over the internet using Cloudflare Tunnel.",
|
||||
ArgsUsage: " ",
|
||||
Description: `Argo Tunnel asks you to specify a hostname on a Cloudflare-powered
|
||||
Description: `Cloudflare Tunnel asks you to specify a hostname on a Cloudflare-powered
|
||||
domain you control and a local address. Traffic from that hostname is routed
|
||||
(optionally via a Cloudflare Load Balancer) to this machine and appears on the
|
||||
specified port where it can be served.
|
||||
|
||||
This feature requires your Cloudflare account be subscribed to the Argo Smart Routing feature.
|
||||
This feature requires your Cloudflare account be subscribed to the Cloudflare Smart Routing feature.
|
||||
|
||||
To use, begin by calling login to download a certificate:
|
||||
|
||||
@@ -488,7 +488,7 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
|
||||
credentialsFileFlag,
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: "is-autoupdated",
|
||||
Usage: "Signal the new process that Argo Tunnel connector has been autoupdated",
|
||||
Usage: "Signal the new process that Cloudflare Tunnel connector has been autoupdated",
|
||||
Value: false,
|
||||
Hidden: true,
|
||||
}),
|
||||
|
||||
@@ -238,7 +238,7 @@ func prepareTunnelConfig(
|
||||
log.Info().Msgf("Warp-routing is enabled")
|
||||
}
|
||||
|
||||
protocolSelector, err := connection.NewProtocolSelector(c.String("protocol"), warpRoutingEnabled, namedTunnel, edgediscovery.HTTP2Percentage, origin.ResolveTTL, log)
|
||||
protocolSelector, err := connection.NewProtocolSelector(c.String("protocol"), warpRoutingEnabled, namedTunnel, edgediscovery.ProtocolPercentage, origin.ResolveTTL, log)
|
||||
if err != nil {
|
||||
return nil, ingress.Ingress{}, err
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ func RunQuickTunnel(sc *subcommandContext) error {
|
||||
sc.log.Info().Msg(line)
|
||||
}
|
||||
|
||||
if !sc.c.IsSet("protocol") {
|
||||
sc.c.Set("protocol", "quic")
|
||||
}
|
||||
|
||||
return StartServer(
|
||||
sc.c,
|
||||
version,
|
||||
|
||||
@@ -151,7 +151,7 @@ func (sc *subcommandContext) readTunnelCredentials(credFinder CredFinder) (conne
|
||||
func (sc *subcommandContext) create(name string, credentialsFilePath string) (*tunnelstore.Tunnel, error) {
|
||||
client, err := sc.client()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't create client to talk to Argo Tunnel backend")
|
||||
return nil, errors.Wrap(err, "couldn't create client to talk to Cloudflare Tunnel backend")
|
||||
}
|
||||
|
||||
tunnelSecret, err := generateTunnelSecret()
|
||||
@@ -230,7 +230,7 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error {
|
||||
for _, id := range tunnelIDs {
|
||||
tunnel, err := client.GetTunnel(id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Can't get tunnel information. Please check tunnel id: %s", tunnel.ID)
|
||||
return errors.Wrapf(err, "Can't get tunnel information. Please check tunnel id: %s", id)
|
||||
}
|
||||
|
||||
// Check if tunnel DeletedAt field has already been set
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
const (
|
||||
DefaultCheckUpdateFreq = time.Hour * 24
|
||||
noUpdateInShellMessage = "cloudflared will not automatically update when run from the shell. To enable auto-updates, run cloudflared as a service: https://developers.cloudflare.com/argo-tunnel/reference/service/"
|
||||
noUpdateInShellMessage = "cloudflared will not automatically update when run from the shell. To enable auto-updates, run cloudflared as a service: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/run-as-service"
|
||||
noUpdateOnWindowsMessage = "cloudflared will not automatically update on Windows systems."
|
||||
noUpdateManagedPackageMessage = "cloudflared will not automatically update if installed by a package manager."
|
||||
isManagedInstallFile = ".installedFromPackageManager"
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
|
||||
const (
|
||||
windowsServiceName = "Cloudflared"
|
||||
windowsServiceDescription = "Argo Tunnel agent"
|
||||
windowsServiceUrl = "https://developers.cloudflare.com/argo-tunnel/reference/service/"
|
||||
windowsServiceDescription = "Cloudflare Tunnel agent"
|
||||
windowsServiceUrl = "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/run-tunnel/run-as-service#windows"
|
||||
|
||||
recoverActionDelay = time.Second * 20
|
||||
failureCountResetPeriod = time.Hour * 24
|
||||
@@ -45,16 +45,16 @@ const (
|
||||
func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
app.Commands = append(app.Commands, &cli.Command{
|
||||
Name: "service",
|
||||
Usage: "Manages the Argo Tunnel Windows service",
|
||||
Usage: "Manages the Cloudflare Tunnel Windows service",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install Argo Tunnel as a Windows service",
|
||||
Usage: "Install Cloudflare Tunnel as a Windows service",
|
||||
Action: cliutil.ConfiguredAction(installWindowsService),
|
||||
},
|
||||
{
|
||||
Name: "uninstall",
|
||||
Usage: "Uninstall the Argo Tunnel service",
|
||||
Usage: "Uninstall the Cloudflare Tunnel service",
|
||||
Action: cliutil.ConfiguredAction(uninstallWindowsService),
|
||||
},
|
||||
},
|
||||
@@ -176,7 +176,7 @@ func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeReques
|
||||
func installWindowsService(c *cli.Context) error {
|
||||
zeroLogger := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
|
||||
|
||||
zeroLogger.Info().Msg("Installing Argo Tunnel Windows service")
|
||||
zeroLogger.Info().Msg("Installing Cloudflare Tunnel Windows service")
|
||||
exepath, err := os.Executable()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot find path name that start the process")
|
||||
@@ -198,7 +198,7 @@ func installWindowsService(c *cli.Context) error {
|
||||
return errors.Wrap(err, "Cannot install service")
|
||||
}
|
||||
defer s.Close()
|
||||
log.Info().Msg("Argo Tunnel agent service is installed")
|
||||
log.Info().Msg("Cloudflare Tunnel agent service is installed")
|
||||
err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||
if err != nil {
|
||||
s.Delete()
|
||||
@@ -217,7 +217,7 @@ func uninstallWindowsService(c *cli.Context) error {
|
||||
With().
|
||||
Str(LogFieldWindowsServiceName, windowsServiceName).Logger()
|
||||
|
||||
log.Info().Msg("Uninstalling Argo Tunnel Windows Service")
|
||||
log.Info().Msg("Uninstalling Cloudflare Tunnel Windows Service")
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot establish a connection to the service control manager")
|
||||
@@ -232,7 +232,7 @@ func uninstallWindowsService(c *cli.Context) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot delete service")
|
||||
}
|
||||
log.Info().Msg("Argo Tunnel agent service is uninstalled")
|
||||
log.Info().Msg("Cloudflare Tunnel agent service is uninstalled")
|
||||
err = eventlog.Remove(windowsServiceName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot remove event logger")
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -19,6 +20,7 @@ const (
|
||||
lbProbeUserAgentPrefix = "Mozilla/5.0 (compatible; Cloudflare-Traffic-Manager/1.0; +https://www.cloudflare.com/traffic-manager/;"
|
||||
LogFieldConnIndex = "connIndex"
|
||||
MaxGracePeriod = time.Minute * 3
|
||||
MaxConcurrentStreams = math.MaxUint32
|
||||
)
|
||||
|
||||
var switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols))
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
@@ -60,7 +59,7 @@ func NewHTTP2Connection(
|
||||
return &HTTP2Connection{
|
||||
conn: conn,
|
||||
server: &http2.Server{
|
||||
MaxConcurrentStreams: math.MaxUint32,
|
||||
MaxConcurrentStreams: MaxConcurrentStreams,
|
||||
},
|
||||
config: config,
|
||||
connOptions: connOptions,
|
||||
|
||||
+105
-53
@@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,7 +26,7 @@ const (
|
||||
|
||||
var (
|
||||
// ProtocolList represents a list of supported protocols for communication with the edge.
|
||||
ProtocolList = []Protocol{H2mux, HTTP2, QUIC}
|
||||
ProtocolList = []Protocol{H2mux, HTTP2, HTTP2Warp, QUIC, QUICWarp}
|
||||
)
|
||||
|
||||
type Protocol int64
|
||||
@@ -36,6 +38,12 @@ const (
|
||||
HTTP2
|
||||
// QUIC is used only with named tunnels.
|
||||
QUIC
|
||||
// HTTP2Warp is used only with named tunnels. It's useful for warp-routing where we don't want to fallback to
|
||||
// H2mux on HTTP2 failure to connect.
|
||||
HTTP2Warp
|
||||
//QUICWarp is used only with named tunnels. It's useful for warp-routing where we want to fallback to HTTP2 but
|
||||
// dont' want HTTP2 to fallback to H2mux
|
||||
QUICWarp
|
||||
)
|
||||
|
||||
// Fallback returns the fallback protocol and whether the protocol has a fallback
|
||||
@@ -45,8 +53,12 @@ func (p Protocol) fallback() (Protocol, bool) {
|
||||
return 0, false
|
||||
case HTTP2:
|
||||
return H2mux, true
|
||||
case HTTP2Warp:
|
||||
return 0, false
|
||||
case QUIC:
|
||||
return HTTP2, true
|
||||
case QUICWarp:
|
||||
return HTTP2Warp, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
@@ -56,9 +68,9 @@ func (p Protocol) String() string {
|
||||
switch p {
|
||||
case H2mux:
|
||||
return "h2mux"
|
||||
case HTTP2:
|
||||
case HTTP2, HTTP2Warp:
|
||||
return "http2"
|
||||
case QUIC:
|
||||
case QUIC, QUICWarp:
|
||||
return "quic"
|
||||
default:
|
||||
return fmt.Sprintf("unknown protocol")
|
||||
@@ -71,11 +83,11 @@ func (p Protocol) TLSSettings() *TLSSettings {
|
||||
return &TLSSettings{
|
||||
ServerName: edgeH2muxTLSServerName,
|
||||
}
|
||||
case HTTP2:
|
||||
case HTTP2, HTTP2Warp:
|
||||
return &TLSSettings{
|
||||
ServerName: edgeH2TLSServerName,
|
||||
}
|
||||
case QUIC:
|
||||
case QUIC, QUICWarp:
|
||||
return &TLSSettings{
|
||||
ServerName: edgeQUICServerName,
|
||||
NextProtos: []string{"argotunnel"},
|
||||
@@ -108,29 +120,36 @@ func (s *staticProtocolSelector) Fallback() (Protocol, bool) {
|
||||
}
|
||||
|
||||
type autoProtocolSelector struct {
|
||||
lock sync.RWMutex
|
||||
current Protocol
|
||||
switchThrehold int32
|
||||
fetchFunc PercentageFetcher
|
||||
refreshAfter time.Time
|
||||
ttl time.Duration
|
||||
log *zerolog.Logger
|
||||
lock sync.RWMutex
|
||||
|
||||
current Protocol
|
||||
|
||||
// protocolPool is desired protocols in the order of priority they should be picked in.
|
||||
protocolPool []Protocol
|
||||
|
||||
switchThreshold int32
|
||||
fetchFunc PercentageFetcher
|
||||
refreshAfter time.Time
|
||||
ttl time.Duration
|
||||
log *zerolog.Logger
|
||||
}
|
||||
|
||||
func newAutoProtocolSelector(
|
||||
current Protocol,
|
||||
switchThrehold int32,
|
||||
protocolPool []Protocol,
|
||||
switchThreshold int32,
|
||||
fetchFunc PercentageFetcher,
|
||||
ttl time.Duration,
|
||||
log *zerolog.Logger,
|
||||
) *autoProtocolSelector {
|
||||
return &autoProtocolSelector{
|
||||
current: current,
|
||||
switchThrehold: switchThrehold,
|
||||
fetchFunc: fetchFunc,
|
||||
refreshAfter: time.Now().Add(ttl),
|
||||
ttl: ttl,
|
||||
log: log,
|
||||
current: current,
|
||||
protocolPool: protocolPool,
|
||||
switchThreshold: switchThreshold,
|
||||
fetchFunc: fetchFunc,
|
||||
refreshAfter: time.Now().Add(ttl),
|
||||
ttl: ttl,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,28 +160,39 @@ func (s *autoProtocolSelector) Current() Protocol {
|
||||
return s.current
|
||||
}
|
||||
|
||||
percentage, err := s.fetchFunc()
|
||||
protocol, err := getProtocol(s.protocolPool, s.fetchFunc, s.switchThreshold)
|
||||
if err != nil {
|
||||
s.log.Err(err).Msg("Failed to refresh protocol")
|
||||
return s.current
|
||||
}
|
||||
s.current = protocol
|
||||
|
||||
if s.switchThrehold < percentage {
|
||||
s.current = HTTP2
|
||||
} else {
|
||||
s.current = H2mux
|
||||
}
|
||||
s.refreshAfter = time.Now().Add(s.ttl)
|
||||
return s.current
|
||||
}
|
||||
|
||||
func getProtocol(protocolPool []Protocol, fetchFunc PercentageFetcher, switchThreshold int32) (Protocol, error) {
|
||||
protocolPercentages, err := fetchFunc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, protocol := range protocolPool {
|
||||
protocolPercentage := protocolPercentages.GetPercentage(protocol.String())
|
||||
if protocolPercentage > switchThreshold {
|
||||
return protocol, nil
|
||||
}
|
||||
}
|
||||
|
||||
return protocolPool[len(protocolPool)-1], nil
|
||||
}
|
||||
|
||||
func (s *autoProtocolSelector) Fallback() (Protocol, bool) {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.current.fallback()
|
||||
}
|
||||
|
||||
type PercentageFetcher func() (int32, error)
|
||||
type PercentageFetcher func() (edgediscovery.ProtocolPercents, error)
|
||||
|
||||
func NewProtocolSelector(
|
||||
protocolFlag string,
|
||||
@@ -179,22 +209,34 @@ func NewProtocolSelector(
|
||||
}, nil
|
||||
}
|
||||
|
||||
// warp routing cannot be served over h2mux connections
|
||||
if warpRoutingEnabled {
|
||||
if protocolFlag == H2mux.String() {
|
||||
log.Warn().Msg("Warp routing is not supported in h2mux protocol. Upgrading to http2 to allow it.")
|
||||
}
|
||||
|
||||
if protocolFlag == QUIC.String() {
|
||||
return &staticProtocolSelector{
|
||||
current: QUIC,
|
||||
}, nil
|
||||
}
|
||||
threshold := switchThreshold(namedTunnel.Credentials.AccountTag)
|
||||
fetchedProtocol, err := getProtocol([]Protocol{QUIC, HTTP2}, fetchFunc, threshold)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can set `--protocol h2mux` in your cloudflared command.")
|
||||
return &staticProtocolSelector{
|
||||
current: HTTP2,
|
||||
}, nil
|
||||
}
|
||||
if warpRoutingEnabled {
|
||||
if protocolFlag == H2mux.String() || fetchedProtocol == H2mux {
|
||||
log.Warn().Msg("Warp routing is not supported in h2mux protocol. Upgrading to http2 to allow it.")
|
||||
protocolFlag = HTTP2.String()
|
||||
fetchedProtocol = HTTP2Warp
|
||||
}
|
||||
return selectWarpRoutingProtocols(protocolFlag, fetchFunc, ttl, log, threshold, fetchedProtocol)
|
||||
}
|
||||
|
||||
return selectNamedTunnelProtocols(protocolFlag, fetchFunc, ttl, log, threshold, fetchedProtocol)
|
||||
}
|
||||
|
||||
func selectNamedTunnelProtocols(
|
||||
protocolFlag string,
|
||||
fetchFunc PercentageFetcher,
|
||||
ttl time.Duration,
|
||||
log *zerolog.Logger,
|
||||
threshold int32,
|
||||
protocol Protocol,
|
||||
) (ProtocolSelector, error) {
|
||||
if protocolFlag == H2mux.String() {
|
||||
return &staticProtocolSelector{
|
||||
current: H2mux,
|
||||
@@ -202,31 +244,41 @@ func NewProtocolSelector(
|
||||
}
|
||||
|
||||
if protocolFlag == QUIC.String() {
|
||||
return newAutoProtocolSelector(QUIC, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
return newAutoProtocolSelector(QUIC, []Protocol{QUIC, HTTP2, H2mux}, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
http2Percentage, err := fetchFunc()
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can set `--protocol h2mux` in your cloudflared command.")
|
||||
return &staticProtocolSelector{
|
||||
current: HTTP2,
|
||||
}, nil
|
||||
}
|
||||
if protocolFlag == HTTP2.String() {
|
||||
if http2Percentage < 0 {
|
||||
return newAutoProtocolSelector(H2mux, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
return newAutoProtocolSelector(HTTP2, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
return newAutoProtocolSelector(HTTP2, []Protocol{HTTP2, H2mux}, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
if protocolFlag != autoSelectFlag {
|
||||
return nil, fmt.Errorf("Unknown protocol %s, %s", protocolFlag, AvailableProtocolFlagMessage)
|
||||
}
|
||||
threshold := switchThreshold(namedTunnel.Credentials.AccountTag)
|
||||
if threshold < http2Percentage {
|
||||
return newAutoProtocolSelector(HTTP2, threshold, fetchFunc, ttl, log), nil
|
||||
|
||||
return newAutoProtocolSelector(protocol, []Protocol{QUIC, HTTP2, H2mux}, threshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
func selectWarpRoutingProtocols(
|
||||
protocolFlag string,
|
||||
fetchFunc PercentageFetcher,
|
||||
ttl time.Duration,
|
||||
log *zerolog.Logger,
|
||||
threshold int32,
|
||||
protocol Protocol,
|
||||
) (ProtocolSelector, error) {
|
||||
if protocolFlag == QUIC.String() {
|
||||
return newAutoProtocolSelector(QUICWarp, []Protocol{QUICWarp, HTTP2Warp}, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
return newAutoProtocolSelector(H2mux, threshold, fetchFunc, ttl, log), nil
|
||||
|
||||
if protocolFlag == HTTP2.String() {
|
||||
return newAutoProtocolSelector(HTTP2Warp, []Protocol{HTTP2Warp}, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
if protocolFlag != autoSelectFlag {
|
||||
return nil, fmt.Errorf("Unknown protocol %s, %s", protocolFlag, AvailableProtocolFlagMessage)
|
||||
}
|
||||
|
||||
return newAutoProtocolSelector(protocol, []Protocol{QUICWarp, HTTP2Warp}, threshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
func switchThreshold(accountTag string) int32 {
|
||||
|
||||
+106
-61
@@ -6,6 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -21,29 +23,23 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func mockFetcher(percentage int32) PercentageFetcher {
|
||||
return func() (int32, error) {
|
||||
return percentage, nil
|
||||
}
|
||||
}
|
||||
|
||||
func mockFetcherWithError() PercentageFetcher {
|
||||
return func() (int32, error) {
|
||||
return 0, fmt.Errorf("failed to fetch precentage")
|
||||
func mockFetcher(getError bool, protocolPercent ...edgediscovery.ProtocolPercent) PercentageFetcher {
|
||||
return func() (edgediscovery.ProtocolPercents, error) {
|
||||
if getError {
|
||||
return nil, fmt.Errorf("failed to fetch precentage")
|
||||
}
|
||||
return protocolPercent, nil
|
||||
}
|
||||
}
|
||||
|
||||
type dynamicMockFetcher struct {
|
||||
percentage int32
|
||||
err error
|
||||
protocolPercents edgediscovery.ProtocolPercents
|
||||
err error
|
||||
}
|
||||
|
||||
func (dmf *dynamicMockFetcher) fetch() PercentageFetcher {
|
||||
return func() (int32, error) {
|
||||
if dmf.err != nil {
|
||||
return 0, dmf.err
|
||||
}
|
||||
return dmf.percentage, nil
|
||||
return func() (edgediscovery.ProtocolPercents, error) {
|
||||
return dmf.protocolPercents, dmf.err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +65,7 @@ func TestNewProtocolSelector(t *testing.T) {
|
||||
name: "named tunnel over h2mux",
|
||||
protocol: "h2mux",
|
||||
expectedProtocol: H2mux,
|
||||
fetchFunc: func() (edgediscovery.ProtocolPercents, error) { return nil, nil },
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
@@ -77,28 +74,38 @@ func TestNewProtocolSelector(t *testing.T) {
|
||||
expectedProtocol: HTTP2,
|
||||
hasFallback: true,
|
||||
expectedFallback: H2mux,
|
||||
fetchFunc: mockFetcher(0),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "named tunnel http2 disabled",
|
||||
protocol: "http2",
|
||||
expectedProtocol: H2mux,
|
||||
fetchFunc: mockFetcher(-1),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "named tunnel quic disabled",
|
||||
protocol: "quic",
|
||||
expectedProtocol: HTTP2,
|
||||
// Hasfallback true is because if http2 fails, then we further fallback to h2mux.
|
||||
hasFallback: true,
|
||||
expectedFallback: H2mux,
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}, edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: -1}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "named tunnel auto all http2 disabled",
|
||||
protocol: "auto",
|
||||
expectedProtocol: H2mux,
|
||||
fetchFunc: mockFetcher(-1),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "named tunnel auto to h2mux",
|
||||
protocol: "auto",
|
||||
expectedProtocol: H2mux,
|
||||
fetchFunc: mockFetcher(0),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
@@ -107,36 +114,71 @@ func TestNewProtocolSelector(t *testing.T) {
|
||||
expectedProtocol: HTTP2,
|
||||
hasFallback: true,
|
||||
expectedFallback: H2mux,
|
||||
fetchFunc: mockFetcher(100),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "named tunnel auto to quic",
|
||||
protocol: "auto",
|
||||
expectedProtocol: QUIC,
|
||||
hasFallback: true,
|
||||
expectedFallback: HTTP2,
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "warp routing requesting h2mux",
|
||||
protocol: "h2mux",
|
||||
expectedProtocol: HTTP2,
|
||||
expectedProtocol: HTTP2Warp,
|
||||
hasFallback: false,
|
||||
expectedFallback: H2mux,
|
||||
fetchFunc: mockFetcher(100),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}),
|
||||
warpRoutingEnabled: true,
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "warp routing requesting h2mux picks HTTP2 even if http2 percent is -1",
|
||||
protocol: "h2mux",
|
||||
expectedProtocol: HTTP2Warp,
|
||||
hasFallback: false,
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}),
|
||||
warpRoutingEnabled: true,
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "warp routing http2",
|
||||
protocol: "http2",
|
||||
expectedProtocol: HTTP2,
|
||||
expectedProtocol: HTTP2Warp,
|
||||
hasFallback: false,
|
||||
expectedFallback: H2mux,
|
||||
fetchFunc: mockFetcher(100),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}),
|
||||
warpRoutingEnabled: true,
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "warp routing quic",
|
||||
protocol: "quic",
|
||||
expectedProtocol: QUICWarp,
|
||||
hasFallback: true,
|
||||
expectedFallback: HTTP2Warp,
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}),
|
||||
warpRoutingEnabled: true,
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "warp routing auto",
|
||||
protocol: "auto",
|
||||
expectedProtocol: HTTP2,
|
||||
expectedProtocol: HTTP2Warp,
|
||||
hasFallback: false,
|
||||
expectedFallback: H2mux,
|
||||
fetchFunc: mockFetcher(100),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}),
|
||||
warpRoutingEnabled: true,
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
{
|
||||
name: "warp routing auto- quic",
|
||||
protocol: "auto",
|
||||
expectedProtocol: QUICWarp,
|
||||
hasFallback: true,
|
||||
expectedFallback: HTTP2Warp,
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}, edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}),
|
||||
warpRoutingEnabled: true,
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
},
|
||||
@@ -149,14 +191,14 @@ func TestNewProtocolSelector(t *testing.T) {
|
||||
{
|
||||
name: "named tunnel unknown protocol",
|
||||
protocol: "unknown",
|
||||
fetchFunc: mockFetcher(100),
|
||||
fetchFunc: mockFetcher(false, edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "named tunnel fetch error",
|
||||
protocol: "unknown",
|
||||
fetchFunc: mockFetcherWithError(),
|
||||
protocol: "auto",
|
||||
fetchFunc: mockFetcher(true),
|
||||
namedTunnelConfig: testNamedTunnelConfig,
|
||||
expectedProtocol: HTTP2,
|
||||
wantErr: false,
|
||||
@@ -164,18 +206,20 @@ func TestNewProtocolSelector(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
selector, err := NewProtocolSelector(test.protocol, test.warpRoutingEnabled, test.namedTunnelConfig, test.fetchFunc, testNoTTL, &log)
|
||||
if test.wantErr {
|
||||
assert.Error(t, err, fmt.Sprintf("test %s failed", test.name))
|
||||
} else {
|
||||
assert.NoError(t, err, fmt.Sprintf("test %s failed", test.name))
|
||||
assert.Equal(t, test.expectedProtocol, selector.Current(), fmt.Sprintf("test %s failed", test.name))
|
||||
fallback, ok := selector.Fallback()
|
||||
assert.Equal(t, test.hasFallback, ok, fmt.Sprintf("test %s failed", test.name))
|
||||
if test.hasFallback {
|
||||
assert.Equal(t, test.expectedFallback, fallback, fmt.Sprintf("test %s failed", test.name))
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
selector, err := NewProtocolSelector(test.protocol, test.warpRoutingEnabled, test.namedTunnelConfig, test.fetchFunc, testNoTTL, &log)
|
||||
if test.wantErr {
|
||||
assert.Error(t, err, fmt.Sprintf("test %s failed", test.name))
|
||||
} else {
|
||||
assert.NoError(t, err, fmt.Sprintf("test %s failed", test.name))
|
||||
assert.Equal(t, test.expectedProtocol, selector.Current(), fmt.Sprintf("test %s failed", test.name))
|
||||
fallback, ok := selector.Fallback()
|
||||
assert.Equal(t, test.hasFallback, ok, fmt.Sprintf("test %s failed", test.name))
|
||||
if test.hasFallback {
|
||||
assert.Equal(t, test.expectedFallback, fallback, fmt.Sprintf("test %s failed", test.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,27 +229,27 @@ func TestAutoProtocolSelectorRefresh(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, H2mux, selector.Current())
|
||||
|
||||
fetcher.percentage = 100
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}}
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = 0
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}}
|
||||
assert.Equal(t, H2mux, selector.Current())
|
||||
|
||||
fetcher.percentage = 100
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}}
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.err = fmt.Errorf("failed to fetch")
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = -1
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}}
|
||||
fetcher.err = nil
|
||||
assert.Equal(t, H2mux, selector.Current())
|
||||
|
||||
fetcher.percentage = 0
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}}
|
||||
assert.Equal(t, H2mux, selector.Current())
|
||||
|
||||
fetcher.percentage = 100
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}}
|
||||
assert.Equal(t, QUIC, selector.Current())
|
||||
}
|
||||
|
||||
func TestHTTP2ProtocolSelectorRefresh(t *testing.T) {
|
||||
@@ -214,35 +258,36 @@ func TestHTTP2ProtocolSelectorRefresh(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = 100
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}}
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = 0
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}}
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.err = fmt.Errorf("failed to fetch")
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = -1
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}}
|
||||
fetcher.err = nil
|
||||
assert.Equal(t, H2mux, selector.Current())
|
||||
|
||||
fetcher.percentage = 0
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 0}}
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = 100
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}}
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
|
||||
fetcher.percentage = -1
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: -1}}
|
||||
assert.Equal(t, H2mux, selector.Current())
|
||||
}
|
||||
|
||||
func TestProtocolSelectorRefreshTTL(t *testing.T) {
|
||||
fetcher := dynamicMockFetcher{percentage: 100}
|
||||
fetcher := dynamicMockFetcher{}
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 100}}
|
||||
selector, err := NewProtocolSelector("auto", noWarpRoutingEnabled, testNamedTunnelConfig, fetcher.fetch(), time.Hour, &log)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
assert.Equal(t, QUIC, selector.Current())
|
||||
|
||||
fetcher.percentage = 0
|
||||
assert.Equal(t, HTTP2, selector.Current())
|
||||
fetcher.protocolPercents = edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "quic", Percentage: 0}}
|
||||
assert.Equal(t, QUIC, selector.Current())
|
||||
}
|
||||
|
||||
+19
-1
@@ -158,11 +158,12 @@ func (hrw httpResponseAdapter) WriteErrorResponse(err error) {
|
||||
quicpogs.WriteConnectResponseData(hrw, err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)})
|
||||
}
|
||||
|
||||
func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.Reader) (*http.Request, error) {
|
||||
func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*http.Request, error) {
|
||||
metadata := connectRequest.MetadataMap()
|
||||
dest := connectRequest.Dest
|
||||
method := metadata[HTTPMethodKey]
|
||||
host := metadata[HTTPHostKey]
|
||||
isWebsocket := connectRequest.Type == quicpogs.ConnectionTypeWebsocket
|
||||
|
||||
req, err := http.NewRequest(method, dest, body)
|
||||
if err != nil {
|
||||
@@ -186,6 +187,16 @@ func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.Reader) (
|
||||
if err := setContentLength(req); err != nil {
|
||||
return nil, fmt.Errorf("Error setting content-length: %w", err)
|
||||
}
|
||||
|
||||
// Go's client defaults to chunked encoding after a 200ms delay if the following cases are true:
|
||||
// * the request body blocks
|
||||
// * the content length is not set (or set to -1)
|
||||
// * the method doesn't usually have a body (GET, HEAD, DELETE, ...)
|
||||
// * there is no transfer-encoding=chunked already set.
|
||||
// So, if transfer cannot be chunked and content length is 0, we dont set a request body.
|
||||
if !isWebsocket && !isTransferEncodingChunked(req) && req.ContentLength == 0 {
|
||||
req.Body = nil
|
||||
}
|
||||
stripWebsocketUpgradeHeader(req)
|
||||
return req, err
|
||||
}
|
||||
@@ -197,3 +208,10 @@ func setContentLength(req *http.Request) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func isTransferEncodingChunked(req *http.Request) bool {
|
||||
transferEncodingVal := req.Header.Get("Transfer-Encoding")
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding suggests that this can be a comma
|
||||
// separated value as well.
|
||||
return strings.Contains(strings.ToLower(transferEncodingVal), "chunked")
|
||||
}
|
||||
|
||||
+132
-1
@@ -108,6 +108,10 @@ func TestQUICServer(t *testing.T) {
|
||||
Key: "HttpMethod",
|
||||
Val: "POST",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Content-Length",
|
||||
Val: "24",
|
||||
},
|
||||
},
|
||||
message: []byte("This is the message body"),
|
||||
expectedResponse: []byte("This is the message body"),
|
||||
@@ -297,6 +301,7 @@ func TestBuildHTTPRequest(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
connectRequest *quicpogs.ConnectRequest
|
||||
body io.ReadCloser
|
||||
req *http.Request
|
||||
}{
|
||||
{
|
||||
@@ -341,7 +346,9 @@ func TestBuildHTTPRequest(t *testing.T) {
|
||||
},
|
||||
ContentLength: 514,
|
||||
Host: "cf.host",
|
||||
Body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
{
|
||||
name: "if content length isn't part of request headers, then it's not set",
|
||||
@@ -380,13 +387,137 @@ func TestBuildHTTPRequest(t *testing.T) {
|
||||
},
|
||||
ContentLength: 0,
|
||||
Host: "cf.host",
|
||||
Body: nil,
|
||||
},
|
||||
body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
{
|
||||
name: "if content length is 0, but transfer-encoding is chunked, body is not nil",
|
||||
connectRequest: &quicpogs.ConnectRequest{
|
||||
Dest: "http://test.com",
|
||||
Metadata: []quicpogs.Metadata{
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Another-Header",
|
||||
Val: "Misc",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Transfer-Encoding",
|
||||
Val: "chunked",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHost",
|
||||
Val: "cf.host",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpMethod",
|
||||
Val: "get",
|
||||
},
|
||||
},
|
||||
},
|
||||
req: &http.Request{
|
||||
Method: "get",
|
||||
URL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "test.com",
|
||||
},
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{
|
||||
"Another-Header": []string{"Misc"},
|
||||
"Transfer-Encoding": []string{"chunked"},
|
||||
},
|
||||
ContentLength: 0,
|
||||
Host: "cf.host",
|
||||
Body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
{
|
||||
name: "if content length is 0, but transfer-encoding is gzip,chunked, body is not nil",
|
||||
connectRequest: &quicpogs.ConnectRequest{
|
||||
Dest: "http://test.com",
|
||||
Metadata: []quicpogs.Metadata{
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Another-Header",
|
||||
Val: "Misc",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Transfer-Encoding",
|
||||
Val: "gzip,chunked",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHost",
|
||||
Val: "cf.host",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpMethod",
|
||||
Val: "get",
|
||||
},
|
||||
},
|
||||
},
|
||||
req: &http.Request{
|
||||
Method: "get",
|
||||
URL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "test.com",
|
||||
},
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{
|
||||
"Another-Header": []string{"Misc"},
|
||||
"Transfer-Encoding": []string{"gzip,chunked"},
|
||||
},
|
||||
ContentLength: 0,
|
||||
Host: "cf.host",
|
||||
Body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
{
|
||||
name: "if content length is 0, and connect request is a websocket, body is not nil",
|
||||
connectRequest: &quicpogs.ConnectRequest{
|
||||
Type: quicpogs.ConnectionTypeWebsocket,
|
||||
Dest: "http://test.com",
|
||||
Metadata: []quicpogs.Metadata{
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Another-Header",
|
||||
Val: "Misc",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHost",
|
||||
Val: "cf.host",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpMethod",
|
||||
Val: "get",
|
||||
},
|
||||
},
|
||||
},
|
||||
req: &http.Request{
|
||||
Method: "get",
|
||||
URL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "test.com",
|
||||
},
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{
|
||||
"Another-Header": []string{"Misc"},
|
||||
},
|
||||
ContentLength: 0,
|
||||
Host: "cf.host",
|
||||
Body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
body: io.NopCloser(&bytes.Buffer{}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req, err := buildHTTPRequest(test.connectRequest, nil)
|
||||
req, err := buildHTTPRequest(test.connectRequest, test.body)
|
||||
assert.NoError(t, err)
|
||||
test.req = test.req.WithContext(req.Context())
|
||||
assert.Equal(t, test.req, req)
|
||||
|
||||
+28
-23
@@ -1,45 +1,50 @@
|
||||
package edgediscovery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
protocolRecord = "protocol.argotunnel.com"
|
||||
protocolRecord = "protocol-v2.argotunnel.com"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoProtocolRecord = fmt.Errorf("No TXT record found for %s to determine connection protocol", protocolRecord)
|
||||
)
|
||||
|
||||
func HTTP2Percentage() (int32, error) {
|
||||
// ProtocolPercent represents a single Protocol Percentage combination.
|
||||
type ProtocolPercent struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Percentage int32 `json:"percentage"`
|
||||
}
|
||||
|
||||
// ProtocolPercents represents the preferred distribution ratio of protocols when protocol isn't specified.
|
||||
type ProtocolPercents []ProtocolPercent
|
||||
|
||||
// GetPercentage returns the threshold percentage of a single protocol.
|
||||
func (p ProtocolPercents) GetPercentage(protocol string) int32 {
|
||||
for _, protocolPercent := range p {
|
||||
if strings.ToLower(protocolPercent.Protocol) == strings.ToLower(protocol) {
|
||||
return protocolPercent.Percentage
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ProtocolPercentage returns the ratio of protocols and a specification ratio for their selection.
|
||||
func ProtocolPercentage() (ProtocolPercents, error) {
|
||||
records, err := net.LookupTXT(protocolRecord)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, err
|
||||
}
|
||||
if len(records) == 0 {
|
||||
return 0, errNoProtocolRecord
|
||||
return nil, errNoProtocolRecord
|
||||
}
|
||||
return parseHTTP2Precentage(records[0])
|
||||
}
|
||||
|
||||
// The record looks like http2=percentage
|
||||
func parseHTTP2Precentage(record string) (int32, error) {
|
||||
const key = "http2"
|
||||
slices := strings.Split(record, "=")
|
||||
if len(slices) != 2 {
|
||||
return 0, fmt.Errorf("Malformed TXT record %s, expect http2=percentage", record)
|
||||
}
|
||||
if slices[0] != key {
|
||||
return 0, fmt.Errorf("Incorrect key %s, expect %s", slices[0], key)
|
||||
}
|
||||
percentage, err := strconv.ParseInt(slices[1], 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(percentage), nil
|
||||
|
||||
var protocolsWithPercent ProtocolPercents
|
||||
err = json.Unmarshal([]byte(records[0]), &protocolsWithPercent)
|
||||
return protocolsWithPercent, err
|
||||
}
|
||||
|
||||
@@ -6,75 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHTTP2Percentage(t *testing.T) {
|
||||
_, err := HTTP2Percentage()
|
||||
func TestProtocolPercentage(t *testing.T) {
|
||||
_, err := ProtocolPercentage()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestParseHTTP2Precentage(t *testing.T) {
|
||||
tests := []struct {
|
||||
record string
|
||||
percentage int32
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
record: "http2=-1",
|
||||
percentage: -1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
record: "http2=0",
|
||||
percentage: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
record: "http2=50",
|
||||
percentage: 50,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
record: "http2=100",
|
||||
percentage: 100,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
record: "http2=1000",
|
||||
percentage: 1000,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
record: "http2=10.5",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
record: "http2=10 h2mux=90",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
record: "http2=ten",
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
{
|
||||
record: "h2mux=100",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
record: "http2",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
record: "http2=",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
p, err := parseHTTP2Precentage(test.record)
|
||||
if test.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.Equal(t, test.percentage, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -37,7 +37,7 @@ type OriginUpTime struct {
|
||||
UpTime string `json:"uptime"`
|
||||
}
|
||||
|
||||
const defaultServerName = "the Argo Tunnel test server"
|
||||
const defaultServerName = "the Cloudflare Tunnel test server"
|
||||
const indexTemplate = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -45,10 +45,10 @@ const indexTemplate = `
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||
<title>
|
||||
Argo Tunnel Connection
|
||||
Cloudflare Tunnel Connection
|
||||
</title>
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Argo Tunnel Connection">
|
||||
<meta name="description" content="Cloudflare Tunnel Connection">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}section{display:block}h1{font-size:2em;margin:.67em 0}a{background-color:transparent;-webkit-text-decoration-skip:objects}/* 1 */::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}/* 1 */a,body,dd,div,dl,dt,h1,h4,html,p,section{box-sizing:border-box}.bt{border-top-style:solid;border-top-width:1px}.bl{border-left-style:solid;border-left-width:1px}.b--orange{border-color:#f38020}.br1{border-radius:.125rem}.bw2{border-width:.25rem}.dib{display:inline-block}.sans-serif{font-family:open sans,-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.overflow-x-auto{overflow-x:auto}.code{font-family:Consolas,monaco,monospace}.b{font-weight:700}.fw3{font-weight:300}.fw4{font-weight:400}.fw5{font-weight:500}.fw6{font-weight:600}.lh-copy{line-height:1.5}.link{text-decoration:none}.link,.link:active,.link:focus,.link:hover,.link:link,.link:visited{transition:color .15s ease-in}.link:focus{outline:1px dotted currentColor}.mw-100{max-width:100%}.mw4{max-width:8rem}.mw7{max-width:48rem}.bg-light-gray{background-color:#f7f7f7}.link-hover:hover{background-color:#1f679e}.white{color:#fff}.bg-white{background-color:#fff}.bg-blue{background-color:#408bc9}.pb2{padding-bottom:.5rem}.pb6{padding-bottom:8rem}.pt3{padding-top:1rem}.pt5{padding-top:4rem}.pv2{padding-top:.5rem;padding-bottom:.5rem}.ph3{padding-left:1rem;padding-right:1rem}.ph4{padding-left:2rem;padding-right:2rem}.ml0{margin-left:0}.mb1{margin-bottom:.25rem}.mb2{margin-bottom:.5rem}.mb3{margin-bottom:1rem}.mt5{margin-top:4rem}.ttu{text-transform:uppercase}.f4{font-size:1.25rem}.f5{font-size:1rem}.f6{font-size:.875rem}.f7{font-size:.75rem}.measure{max-width:30em}.center{margin-left:auto}.center{margin-right:auto}@media screen and (min-width:30em){.f2-ns{font-size:2.25rem}}@media screen and (min-width:30em) and (max-width:60em){.f5-m{font-size:1rem}}@media screen and (min-width:60em){.f4-l{font-size:1.25rem}}
|
||||
@@ -66,7 +66,7 @@ const indexTemplate = `
|
||||
</svg>
|
||||
<h1 class="f4 f2-ns mt5 fw5">Congrats! You created a tunnel!</h1>
|
||||
<p class="f6 f5-m f4-l measure lh-copy fw3">
|
||||
Argo Tunnel exposes locally running applications to the internet by
|
||||
Cloudflare Tunnel exposes locally running applications to the internet by
|
||||
running an encrypted, virtual tunnel from your laptop or server to
|
||||
Cloudflare's edge network.
|
||||
</p>
|
||||
@@ -74,7 +74,7 @@ const indexTemplate = `
|
||||
<a
|
||||
class="fw6 link white bg-blue ph4 pv2 br1 dib f5 link-hover"
|
||||
style="border-bottom: 1px solid #1f679e"
|
||||
href="https://developers.cloudflare.com/argo-tunnel/">
|
||||
href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps">
|
||||
Get started here
|
||||
</a>
|
||||
<section>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
basename: "cloudflared"
|
||||
comment: "Cloudflare Argo Tunnel"
|
||||
comment: "Cloudflare Tunnel"
|
||||
copyright: "Cloudflare, Inc"
|
||||
arch: "x86"
|
||||
abi: "64"
|
||||
|
||||
+7
-7
@@ -351,18 +351,17 @@ func serveTunnel(
|
||||
)
|
||||
|
||||
switch protocol {
|
||||
case connection.QUIC:
|
||||
case connection.QUIC, connection.QUICWarp:
|
||||
connOptions := config.ConnectionOptions(addr.UDP.String(), uint8(backoff.Retries()))
|
||||
return ServeQUIC(ctx,
|
||||
addr.UDP,
|
||||
config,
|
||||
connOptions,
|
||||
controlStream,
|
||||
connectedFuse,
|
||||
reconnectCh,
|
||||
gracefulShutdownC)
|
||||
|
||||
case connection.HTTP2:
|
||||
case connection.HTTP2, connection.HTTP2Warp:
|
||||
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr.TCP)
|
||||
if err != nil {
|
||||
connLog.Err(err).Msg("Unable to establish connection with Cloudflare edge")
|
||||
@@ -509,15 +508,16 @@ func ServeQUIC(
|
||||
config *TunnelConfig,
|
||||
connOptions *tunnelpogs.ConnectionOptions,
|
||||
controlStreamHandler connection.ControlStreamHandler,
|
||||
connectedFuse connection.ConnectedFuse,
|
||||
reconnectCh chan ReconnectSignal,
|
||||
gracefulShutdownC <-chan struct{},
|
||||
) (err error, recoverable bool) {
|
||||
tlsConfig := config.EdgeTLSConfigs[connection.QUIC]
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: quicHandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicMaxIdleTimeout,
|
||||
KeepAlive: true,
|
||||
HandshakeIdleTimeout: quicHandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicMaxIdleTimeout,
|
||||
MaxIncomingStreams: connection.MaxConcurrentStreams,
|
||||
MaxIncomingUniStreams: connection.MaxConcurrentStreams,
|
||||
KeepAlive: true,
|
||||
}
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -8,20 +8,18 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/retry"
|
||||
)
|
||||
|
||||
type dynamicMockFetcher struct {
|
||||
percentage int32
|
||||
err error
|
||||
protocolPercents edgediscovery.ProtocolPercents
|
||||
err error
|
||||
}
|
||||
|
||||
func (dmf *dynamicMockFetcher) fetch() connection.PercentageFetcher {
|
||||
return func() (int32, error) {
|
||||
if dmf.err != nil {
|
||||
return 0, dmf.err
|
||||
}
|
||||
return dmf.percentage, nil
|
||||
return func() (edgediscovery.ProtocolPercents, error) {
|
||||
return dmf.protocolPercents, dmf.err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +37,7 @@ func TestWaitForBackoffFallback(t *testing.T) {
|
||||
},
|
||||
}
|
||||
mockFetcher := dynamicMockFetcher{
|
||||
percentage: 0,
|
||||
protocolPercents: edgediscovery.ProtocolPercents{edgediscovery.ProtocolPercent{Protocol: "http2", Percentage: 100}},
|
||||
}
|
||||
warpRoutingEnabled := false
|
||||
protocolSelector, err := connection.NewProtocolSelector(
|
||||
|
||||
@@ -148,7 +148,7 @@ func validateScheme(scheme string) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Currently Argo Tunnel does not support %s protocol.", scheme)
|
||||
return fmt.Errorf("Currently Cloudflare Tunnel does not support %s protocol.", scheme)
|
||||
}
|
||||
|
||||
func validateIP(scheme, host, port string) (string, error) {
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestValidateUrl(t *testing.T) {
|
||||
assert.Empty(t, validUrl)
|
||||
|
||||
validUrl, err = ValidateUrl("ftp://alex:12345@hello.example.com:8080/robot.txt")
|
||||
assert.Equal(t, "Currently Argo Tunnel does not support ftp protocol.", err.Error())
|
||||
assert.Equal(t, "Currently Cloudflare Tunnel does not support ftp protocol.", err.Error())
|
||||
assert.Empty(t, validUrl)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user