Skip to content

Commit 3f30051

Browse files
committed
Merge branch 'rcknr-cloudflared'
2 parents fc1b23b + ea57d98 commit 3f30051

File tree

6 files changed

+154
-113
lines changed

6 files changed

+154
-113
lines changed

cli/Valet/Cloudflared.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Valet;
4+
5+
use GuzzleHttp\Client;
6+
7+
class Cloudflared
8+
{
9+
public function __construct(public CommandLine $cli, public Brew $brew)
10+
{
11+
}
12+
13+
public function currentTunnelUrl(string $domain): ?string
14+
{
15+
// Every cloudflared process will start a "metrics" web server where the
16+
// Quick Tunnel URL will be mentioned under the /metrics endpoint; there
17+
// can potentially be more than one process that matches, but the lsof
18+
// command will show which one is actually functionally running
19+
foreach (array_filter(explode(PHP_EOL, $this->cli->run('pgrep -fl cloudflared'))) as $process) {
20+
// Get the URL shared in this process
21+
preg_match('/(?<pid>\d+)\s.+--http-host-header\s(?<domain>[^\s]+).*/', $process, $pgrepMatches);
22+
23+
if (! array_key_exists('domain', $pgrepMatches) || ! array_key_exists('pid', $pgrepMatches)) {
24+
continue;
25+
}
26+
27+
if ($pgrepMatches['domain'] !== $domain) {
28+
continue;
29+
}
30+
31+
// Get the localhost URL for the metrics server
32+
$lsof = $this->cli->run("lsof -iTCP -P -a -p {$pgrepMatches['pid']}");
33+
preg_match('/TCP\s(?<server>[^\s]+:\d+)\s\(LISTEN\)/', $lsof, $lsofMatches);
34+
35+
if (! array_key_exists('server', $lsofMatches)) {
36+
continue;
37+
}
38+
39+
try {
40+
// Get the cloudflared share URL from the metrics server output
41+
$body = (new Client())->get("http://{$lsofMatches['server']}/metrics")->getBody();
42+
preg_match('/userHostname="(?<url>.+)"/', $body->getContents(), $userHostnameMatches);
43+
} catch (\Exception $e) {
44+
return false;
45+
}
46+
47+
if (array_key_exists('url', $userHostnameMatches)) {
48+
return $userHostnameMatches['url'];
49+
}
50+
}
51+
52+
return false;
53+
}
54+
55+
/**
56+
* Return whether cloudflared is installed.
57+
*/
58+
public function installed(): bool
59+
{
60+
return $this->brew->installed('cloudflared');
61+
}
62+
63+
/**
64+
* Make sure cloudflared is installed.
65+
*/
66+
public function ensureInstalled(): void
67+
{
68+
$this->brew->ensureInstalled('cloudflared');
69+
}
70+
}

cli/Valet/Expose.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Expose
99
{
1010
public function __construct(public Composer $composer, public CommandLine $cli) {}
1111

12-
public function currentTunnelUrl(?string $domain = null): ?string
12+
public function currentTunnelUrl(string $domain): ?string
1313
{
1414
$endpoint = 'http://127.0.0.1:4040/api/tunnels';
1515

cli/Valet/Ngrok.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public function __construct(public CommandLine $cli, public Brew $brew) {}
1818
/**
1919
* Get the current tunnel URL from the Ngrok API.
2020
*/
21-
public function currentTunnelUrl(?string $domain = null): string
21+
public function currentTunnelUrl(string $domain): string
2222
{
2323
// wait a second for ngrok to start before attempting to find available tunnels
2424
sleep(1);

cli/app.php

Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ function (ConsoleCommandEvent $event) {
4747

4848
Upgrader::onEveryRun();
4949

50+
$share_tools = [
51+
'cloudflared',
52+
'expose',
53+
'ngrok'
54+
];
55+
5056
/**
5157
* Install Valet and any required services.
5258
*/
@@ -378,83 +384,60 @@ function (ConsoleCommandEvent $event) {
378384
/**
379385
* Echo the currently tunneled URL.
380386
*/
381-
$app->command('fetch-share-url [domain]', function ($domain = null) {
387+
$app->command('fetch-share-url [domain]', function ($domain = null) use ($share_tools) {
382388
$tool = Configuration::read()['share-tool'] ?? null;
383389

384-
switch ($tool) {
385-
case 'expose':
386-
if ($url = Expose::currentTunnelUrl($domain ?: Site::host(getcwd()))) {
387-
output($url);
388-
}
389-
break;
390-
case 'ngrok':
391-
try {
392-
output(Ngrok::currentTunnelUrl(Site::domain($domain)));
393-
} catch (\Throwable $e) {
394-
warning($e->getMessage());
395-
}
396-
break;
397-
default:
398-
info('Please set your share tool with `valet share-tool expose` or `valet share-tool ngrok`.');
390+
if ($tool && in_array($tool, $share_tools) && class_exists($tool)) {
391+
try {
392+
output($tool::currentTunnelUrl(Site::domain($domain)));
393+
} catch (\Throwable $e) {
394+
warning($e->getMessage());
395+
}
396+
} else {
397+
info('Please set your share tool with `valet share-tool`.');
399398

400-
return Command::FAILURE;
399+
return Command::FAILURE;
401400
}
402-
})->descriptions('Get the URL to the current share tunnel (for Expose or ngrok)');
401+
})->descriptions('Get the URL to the current share tunnel');
403402

404403
/**
405404
* Echo or set the name of the currently-selected share tool (either "ngrok" or "expose").
406405
*/
407-
$app->command('share-tool [tool]', function (InputInterface $input, OutputInterface $output, $tool = null) {
406+
$app->command('share-tool [tool]', function (InputInterface $input, OutputInterface $output, $tool = null)
407+
use ($share_tools) {
408408
if ($tool === null) {
409409
return output(Configuration::read()['share-tool'] ?? '(not set)');
410410
}
411411

412-
if ($tool !== 'expose' && $tool !== 'ngrok') {
413-
warning($tool.' is not a valid share tool. Please use `ngrok` or `expose`.');
412+
$share_tools_list = preg_replace('/,\s([^,]+)$/', ' or $1',
413+
join(', ', array_map(fn($t) => "`$t`", $share_tools)));
414+
415+
if (! in_array($tool, $share_tools) || ! class_exists($tool)) {
416+
warning("$tool is not a valid share tool. Please use $share_tools_list.");
414417

415418
return Command::FAILURE;
416419
}
417420

418421
Configuration::updateKey('share-tool', $tool);
419-
info('Share tool set to '.$tool.'.');
420-
421-
if ($tool === 'expose') {
422-
if (Expose::installed()) {
423-
// @todo: Check it's the right version (has /api/tunnels/)
424-
// E.g. if (Expose::installedVersion)
425-
// if (version_compare(Expose::installedVersion(), $minimumExposeVersion) < 0) {
426-
// prompt them to upgrade
427-
return;
428-
}
422+
info("Share tool set to $tool.");
429423

424+
if (! $tool::installed()) {
430425
$helper = $this->getHelperSet()->get('question');
431-
$question = new ConfirmationQuestion('Would you like to install Expose now? [y/N] ', false);
426+
$question = new ConfirmationQuestion(
427+
'Would you like to install '.ucfirst($tool).' now? [y/N] ',
428+
false);
432429

433430
if ($helper->ask($input, $output, $question) === false) {
434-
info('Proceeding without installing Expose.');
431+
info('Proceeding without installing '.ucfirst($tool).'.');
435432

436433
return;
437434
}
438435

439-
Expose::ensureInstalled();
440-
441-
return;
436+
$tool::ensureInstalled();
442437
}
443438

444-
if (! Ngrok::installed()) {
445-
info("\nIn order to share with ngrok, you'll need a version\nof ngrok installed and managed by Homebrew.");
446-
$helper = $this->getHelperSet()->get('question');
447-
$question = new ConfirmationQuestion('Would you like to install ngrok via Homebrew now? [y/N] ', false);
448-
449-
if ($helper->ask($input, $output, $question) === false) {
450-
info('Proceeding without installing ngrok.');
451-
452-
return;
453-
}
454-
455-
Ngrok::ensureInstalled();
456-
}
457-
})->descriptions('Get the name of the current share tool (Expose or ngrok).');
439+
return Command::SUCCESS;
440+
})->descriptions('Get the name of the current share tool.');
458441

459442
/**
460443
* Set the ngrok auth token.

cli/includes/facades.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ class Site extends Facade {}
3838
class Status extends Facade {}
3939
class Upgrader extends Facade {}
4040
class Valet extends Facade {}
41+
class Cloudflared extends Facade {}

valet

Lines changed: 48 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,41 @@ else
4141
fi
4242

4343
# If the command is the "share" command we will need to resolve out any
44-
# symbolic links for the site. Before starting Ngrok, we will fire a
45-
# process to retrieve the live Ngrok tunnel URL in the background.
44+
# symbolic links for the site. Before starting the share tool, we will fire a
45+
# process to retrieve the live the share tool tunnel URL in the background.
4646
if [[ "$1" = "share" ]]
4747
then
4848
SHARETOOL="$("$PHP" "$DIR/cli/valet.php" share-tool)"
4949

50+
# Check for parameters to pass through to share tool (these will start with '-' or '--')
51+
PARAMS=(${@:2})
52+
53+
for PARAM in ${PARAMS[@]}
54+
do
55+
if [[ ${PARAM:0:1} == '-' ]]; then
56+
PARAMS=("${PARAMS[@]/$PARAM}") # Quotes when working with strings
57+
fi
58+
done
59+
60+
PARAMS=${PARAMS[@]}
61+
62+
HOST="${PWD##*/}"
63+
64+
# Find the first linked site for the current dir, if one exists
65+
for linkname in ~/.config/valet/Sites/*; do
66+
if [[ "$(readlink $linkname)" = "$PWD" ]]
67+
then
68+
HOST="${linkname##*/}"
69+
break
70+
fi
71+
done
72+
73+
# Lowercase the host to match how the rest of our domains are looked up
74+
HOST=$(echo "$HOST" | tr '[:upper:]' '[:lower:]')
75+
TLD=$("$PHP" "$DIR/cli/valet.php" tld)
76+
$(grep --quiet --no-messages 443 ~/.config/valet/Nginx/$HOST*)
77+
SECURED=$?
78+
5079
if [[ $SHARETOOL = "ngrok" ]]
5180
then
5281
# ngrok
@@ -59,41 +88,13 @@ then
5988
exit
6089
fi
6190

62-
# Check for parameters to pass through to ngrok (these will start with '-' or '--')
63-
PARAMS=(${@:2})
64-
for PARAM in ${PARAMS[@]}
65-
do
66-
if [[ ${PARAM:0:1} != '-' ]]; then
67-
PARAMS=("${PARAMS[@]/$PARAM}") # Quotes when working with strings
68-
fi
69-
done
70-
71-
PARAMS=${PARAMS[@]}
72-
73-
HOST="${PWD##*/}"
74-
75-
# Find the first linked site for the current dir, if one exists
76-
for linkname in ~/.config/valet/Sites/*; do
77-
if [[ "$(readlink $linkname)" = "$PWD" ]]
78-
then
79-
HOST="${linkname##*/}"
80-
break
81-
fi
82-
done
83-
84-
TLD=$("$PHP" "$DIR/cli/valet.php" tld)
85-
8691
# Decide the correct PORT: uses 60 for secure, else 80
87-
if grep --quiet --no-messages 443 ~/.config/valet/Nginx/$HOST*
88-
then
92+
if [[ $SECURED -eq 0 ]]; then
8993
PORT=60
9094
else
9195
PORT=80
9296
fi
9397

94-
# Lowercase the host to match how the rest of our domains are looked up
95-
HOST=$(echo "$HOST" | tr '[:upper:]' '[:lower:]')
96-
9798
sudo -u "$USER" "$BREW_PREFIX/bin/ngrok" http "$HOST.$TLD:$PORT" --host-header=rewrite $PARAMS
9899

99100
exit
@@ -102,48 +103,34 @@ then
102103
then
103104

104105
# expose
105-
# Check for parameters to pass through to Expose (these will start with '-' or '--')
106-
PARAMS=(${@:2})
107-
for PARAM in ${PARAMS[@]}
108-
do
109-
if [[ ${PARAM:0:1} != '-' ]]; then
110-
PARAMS=("${PARAMS[@]/$PARAM}") #Quotes when working with strings
111-
fi
112-
done
113-
114-
PARAMS=${PARAMS[@]}
115-
116-
HOST="${PWD##*/}"
117-
118-
# Find the first linked site for the current dir, if one exists
119-
for linkname in ~/.config/valet/Sites/*; do
120-
if [[ "$(readlink $linkname)" = "$PWD" ]]
121-
then
122-
HOST="${linkname##*/}"
123-
break
124-
fi
125-
done
126-
127-
TLD=$("$PHP" "$DIR/cli/valet.php" tld)
128-
129106
# Decide the correct PORT: uses 443 for secure, else 80
130-
if grep --quiet --no-messages 443 ~/.config/valet/Nginx/$HOST*
131-
then
107+
if [[ $SECURED -eq 0 ]]; then
132108
PORT=443
133109
else
134110
PORT=80
135111
fi
136112

137-
# Lowercase the host to match how the rest of our domains are looked up
138-
HOST=$(echo "$HOST" | tr '[:upper:]' '[:lower:]')
139-
140113
sudo -u "$USER" expose share "$HOST.$TLD:$PORT" $PARAMS
141114

142115
exit
143116

117+
elif [[ $SHARETOOL = "cloudflared" ]]
118+
then
119+
# cloudflared
120+
if [[ $SECURED -eq 0 ]]; then
121+
SCHEME="https"
122+
else
123+
SCHEME="http"
124+
fi
125+
126+
sudo -u "$USER" cloudflared tunnel --no-tls-verify --url "$SCHEME://localhost" \
127+
--http-host-header "$HOST.$TLD" $PARAMS
128+
129+
exit
130+
144131
else
145132
echo ''
146-
echo "Please use 'valet share-tool ngrok' or 'valet share-tool expose'"
133+
echo "Please use 'valet share-tool cloudflared', 'valet share-tool expose' or 'valet share-tool ngrok'"
147134
echo "to set your preferred share tool."
148135
exit
149136
fi

0 commit comments

Comments
 (0)