Skip to content

Commit

Permalink
Added host and extra options
Browse files Browse the repository at this point in the history
  • Loading branch information
jn-jairo committed Mar 3, 2022
1 parent 7930684 commit f2ac0be
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 87 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG-2.x.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog

## [Unreleased](https://github.com/jn-jairo/laravel-ngrok/compare/v2.0.2...2.x)
## [Unreleased](https://github.com/jn-jairo/laravel-ngrok/compare/v2.0.3...2.x)

## [v2.0.3 (2022-03-03)](https://github.com/jn-jairo/laravel-ngrok/compare/v2.0.2...v2.0.3)

### Added
- `--host` and `--extra` options
- Allow ngrok url with region

## [v2.0.2 (2022-02-11)](https://github.com/jn-jairo/laravel-ngrok/compare/v2.0.1...v2.0.2)

Expand Down
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,48 @@ php artisan ngrok

The parameters for ngrok will be extracted from your application.

You can also pass custom host and port.
## Advanced usage

```bash
php artisan ngrok example.com --port=8000
php artisan ngrok [options] [--] [<host-header>]
```

Argument | Description
:----------------|:------------------------------------------------------
**host-header** | Host header to identify the app (Example: myapp.test)

Option | Description
:------------------------|:-----------------------------------------------------------
**-H, --host[=HOST]** | Host to tunnel the requests (default: localhost)
**-P, --port[=PORT]** | Port to tunnel the requests (default: 80)
**-E, --extra[=EXTRA]** | Extra arguments to ngrok command (multiple values allowed)


## Examples

```bash
# If you have multiples apps (myapp.test, my-other-app.test, ...)
# set it in the app.url configuration
# or pass it in the host-header argument

php artisan ngrok myapp.test

# If you use a different port, set it in the app.url configuration
# or pass it in the --port option

php artisan ngrok --port=8000 myapp.test

# If you use docker and have containers like (nginx, php, workspace)
# and wanna run the command inside the workspace container
# pass the name of the container the requests will tunnel through

php artisan ngrok --host=nginx example.com

# If you wanna pass other arguments directly to ngrok
# use the --extra or -E option

php artisan ngrok --extra='--region=eu' -E'--config=ngrok.yml'

```

## License
Expand Down
28 changes: 19 additions & 9 deletions src/NgrokCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ class NgrokCommand extends Command
* @var string
*/
protected $signature = 'ngrok
{host? : The host to share}
{--port= : The port to share}';
{host-header? : Host header to identify the app (Example: myapp.test)}
{--H|host= : Host to tunnel the requests (default: localhost)}
{--P|port= : Port to tunnel the requests (default: 80)}
{--E|extra=* : Extra arguments to ngrok command}';

/**
* The console command description.
Expand Down Expand Up @@ -58,17 +60,19 @@ public function __construct(NgrokProcessBuilder $processBuilder, NgrokWebService
*/
public function handle() : int
{
$host = $this->argument('host');
$hostHeader = $this->argument('host-header');
$host = $this->option('host');
$port = $this->option('port');
$extra = $this->option('extra');

if ($host === null) {
if ($hostHeader === null) {
$url = $this->getLaravel()->make('config')->get('app.url');

$urlParsed = parse_url($url);

if ($urlParsed !== false) {
if (isset($urlParsed['host'])) {
$host = $urlParsed['host'];
$hostHeader = $urlParsed['host'];
}

if (isset($urlParsed['port']) && $port === null) {
Expand All @@ -77,11 +81,12 @@ public function handle() : int
}
}

if (empty($host)) {
$this->error('Invalid host');
if (empty($hostHeader)) {
$this->error('Invalid host header');
return 1;
}

$host = $host ?: 'localhost';
$port = $port ?: '80';

$this->line('-----------------');
Expand All @@ -90,12 +95,17 @@ public function handle() : int

$this->line('');

$this->line('<fg=green>Host header: </fg=green>' . $hostHeader);
$this->line('<fg=green>Host: </fg=green>' . $host);
$this->line('<fg=green>Port: </fg=green>' . $port);

if (! empty($extra)) {
$this->line('<fg=green>Extra: </fg=green>' . implode(' ', $extra));
}

$this->line('');

$process = $this->processBuilder->buildProcess($host, $port);
$process = $this->processBuilder->buildProcess($hostHeader, $port, $host, $extra);

return $this->runProcess($process);
}
Expand Down Expand Up @@ -129,7 +139,7 @@ private function runProcess(Process $process) : int
if ($webServiceStarted && ! $tunnelStarted) {
$tunnels = $webService->getTunnels();

if (! empty($tunnels)) {
if (! empty($tunnels) && count($tunnels) > 1) {
$tunnelStarted = true;

foreach ($tunnels as $tunnel) {
Expand Down
24 changes: 18 additions & 6 deletions src/NgrokProcessBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,32 @@ public function getWorkingDirectory() : string
/**
* Build ngrok command.
*
* @param string $host
* @param string $hostHeader
* @param string $port
* @param string $host
* @param array $extra
* @return \Symfony\Component\Process\Process
*/
public function buildProcess(string $host = '', string $port = '80') : Process
{
public function buildProcess(
string $hostHeader = '',
string $port = '80',
string $host = '',
array $extra = []
) : Process {
$command = ['ngrok', 'http', '--log', 'stdout'];

if ($host !== '') {
$command = array_merge($command, $extra);

if ($hostHeader !== '') {
$command[] = '--host-header';
$command[] = $host;
$command[] = $hostHeader;
}

$command[] = $port ?: '80';
if ($host !== '') {
$command[] = $host . ':' . ($port ?: '80');
} else {
$command[] = $port ?: '80';
}

return new Process($command, $this->getWorkingDirectory(), null, null, null);
}
Expand Down
2 changes: 1 addition & 1 deletion src/NgrokServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,6 @@ private function extractOriginalHost(Request $request) : string
*/
private function isNgrokHost(string $host) : bool
{
return preg_match('/^[-a-z0-9]+\.ngrok\.io$/i', $host);
return preg_match('/^[\.\-a-z0-9]+\.ngrok\.io$/i', $host);
}
}
90 changes: 82 additions & 8 deletions tests/NgrokCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ class NgrokCommandTest extends TestCase

public function test_handle() : void
{
$host = 'example.com';
$hostHeader = 'example.com';
$port = '80';
$host = 'localhost';
$extra = [];

config(['app.url' => '']);

Expand Down Expand Up @@ -56,21 +58,90 @@ public function test_handle() : void
$process->getExitCode()->willReturn(0)->shouldBeCalled();

$processBuilder = $this->prophesize(NgrokProcessBuilder::class);
$processBuilder->buildProcess($host, $port)->willReturn($process->reveal())->shouldBeCalled();
$processBuilder->buildProcess($hostHeader, $port, $host, $extra)->willReturn($process->reveal())->shouldBeCalled();

app()->instance(NgrokWebService::class, $webService->reveal());
app()->instance(NgrokProcessBuilder::class, $processBuilder->reveal());

$this->artisan('ngrok', ['host' => $host, '--port' => $port])
$this->artisan('ngrok', [
'host-header' => $hostHeader,
'--host' => $host,
'--port' => $port,
])
->expectsOutput('Host header: ' . $hostHeader)
->expectsOutput('Host: ' . $host)
->expectsOutput('Port: ' . $port)
->assertExitCode(0);
}

public function test_handle_extra() : void
{
$hostHeader = 'example.com';
$port = '80';
$host = 'nginx';
$extra = ['--region=eu', '--config=ngrok.yml'];
$extraString = '--region=eu --config=ngrok.yml';

config(['app.url' => '']);

$tunnels = [
[
'public_url' => 'http://0000-0000.ngrok.io',
'config' => ['addr' => 'nginx:80'],
],
[
'public_url' => 'https://0000-0000.ngrok.io',
'config' => ['addr' => 'nginx:80'],
],
];

$webService = $this->prophesize(NgrokWebService::class);
$webService->setUrl('http://127.0.0.1:4040')->shouldBeCalled();
$webService->getTunnels()->willReturn($tunnels)->shouldBeCalled();

$process = $this->prophesize(Process::class);
$process->run(\Prophecy\Argument::type('callable'))->will(function ($args) use ($process) {
$callback = $args[0];

$process->getOutput()->willReturn('msg="starting web service" addr=127.0.0.1:4040')->shouldBeCalled();
$process->clearOutput()->willReturn($process)->shouldBeCalled();

$callback(Process::OUT, 'msg="starting web service" addr=127.0.0.1:4040');

$process->clearErrorOutput()->willReturn($process)->shouldBeCalled();

$callback(Process::ERR, 'error');

return 0;
})->shouldBeCalled();
$process->getErrorOutput()->willReturn('')->shouldBeCalled();
$process->getExitCode()->willReturn(0)->shouldBeCalled();

$processBuilder = $this->prophesize(NgrokProcessBuilder::class);
$processBuilder->buildProcess($hostHeader, $port, $host, $extra)->willReturn($process->reveal())->shouldBeCalled();

app()->instance(NgrokWebService::class, $webService->reveal());
app()->instance(NgrokProcessBuilder::class, $processBuilder->reveal());

$this->artisan('ngrok', [
'host-header' => $hostHeader,
'--host' => $host,
'--port' => $port,
'--extra' => $extra,
])
->expectsOutput('Host header: ' . $hostHeader)
->expectsOutput('Host: ' . $host)
->expectsOutput('Port: ' . $port)
->expectsOutput('Extra: ' . $extraString)
->assertExitCode(0);
}

public function test_handle_from_config() : void
{
$host = 'example.com';
$hostHeader = 'example.com';
$port = '8000';
$host = 'localhost';
$extra = [];

config(['app.url' => 'http://example.com:8000']);

Expand Down Expand Up @@ -108,21 +179,24 @@ public function test_handle_from_config() : void
$process->getExitCode()->willReturn(0)->shouldBeCalled();

$processBuilder = $this->prophesize(NgrokProcessBuilder::class);
$processBuilder->buildProcess($host, $port)->willReturn($process->reveal())->shouldBeCalled();
$processBuilder->buildProcess($hostHeader, $port, $host, $extra)->willReturn($process->reveal())->shouldBeCalled();

app()->instance(NgrokWebService::class, $webService->reveal());
app()->instance(NgrokProcessBuilder::class, $processBuilder->reveal());

$this->artisan('ngrok')
->expectsOutput('Host header: ' . $hostHeader)
->expectsOutput('Host: ' . $host)
->expectsOutput('Port: ' . $port)
->assertExitCode(0);
}

public function test_handle_invalid_host() : void
public function test_handle_invalid_host_header() : void
{
$host = 'example.com';
$hostHeader = 'example.com';
$port = '8000';
$host = 'localhost';
$extra = [];

config(['app.url' => '']);

Expand All @@ -147,7 +221,7 @@ public function test_handle_invalid_host() : void
$process->getExitCode()->willReturn(0)->shouldNotBeCalled();

$processBuilder = $this->prophesize(NgrokProcessBuilder::class);
$processBuilder->buildProcess($host, $port)->willReturn($process->reveal())->shouldNotBeCalled();
$processBuilder->buildProcess($hostHeader, $port, $host, $extra)->willReturn($process->reveal())->shouldNotBeCalled();

app()->instance(NgrokWebService::class, $webService->reveal());
app()->instance(NgrokProcessBuilder::class, $processBuilder->reveal());
Expand Down
Loading

0 comments on commit f2ac0be

Please sign in to comment.