Skip to content

Commit 055a4c3

Browse files
authored
Ngrok v3 (#62)
* authToken related classes polish * Added support for Ngrok v3, added option to still use v2 by enabling `ngrok.legacy` config prop, removed ngrok archive urls from configuration * Closing #56 * Updated docs * Updated docs [skip ci]
1 parent 9e351db commit 055a4c3

17 files changed

+199
-99
lines changed

README.md

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
3333
## What this starter gives to you?
3434
This starter will automatically download Ngrok binary corresponding to your
35-
operating system (Windows, Linux, OSX or even Docker) and then cache it into `home_directory/.ngrok2`.
35+
operating system (Windows, Linux, OSX or even Docker) and then cache it into `home_directory/.ngrok3`.
36+
If Ngrok binary is already present in the PATH, download will be skipped.
3637
Then every time you will run your Spring Boot application, Ngrok will
3738
automatically build http tunnel pointing to your springs web server, and you will get pretty logs
3839
with the remote links, just like it's done below 👇
@@ -48,7 +49,7 @@ Code of demo application available [here](https://github.com/kilmajster/demo).
4849
<dependency>
4950
<groupId>io.github.kilmajster</groupId>
5051
<artifactId>ngrok-spring-boot-starter</artifactId>
51-
<version>0.6.0</version>
52+
<version>0.7.0</version>
5253
</dependency>
5354
```
5455

@@ -76,12 +77,13 @@ If you got already configured auth token in your ngrok config file there is no n
7677
> What will happen now?
7778
>
7879
> If you are using default spring configuration of server port, which is `8080`, then ngrok will
79-
> be downloaded, extracted and cached into user default directory(eg. `/home/user/.ngrok2`) and then executed
80+
> be downloaded, extracted and cached into user default directory(eg. `/home/user/.ngrok3`) and then executed
8081
> on application startup, so final command executed in background as child process, should look like:
8182
> ```bash
82-
> /home/user/.ngrok2/ngrok http 8080
83+
> /home/user/.ngrok3/ngrok http 8080
8384
> ```
8485
> if you are using different server port, it will be picked up automatically from `server.port` property.
86+
> If ngrok binary is already present in path, download will be skipped
8587
8688
### ⚙️ Advanced configuration
8789
#### `ngrok.config` - ngrok configuration file(s)
@@ -95,9 +97,9 @@ ngrok.config=/home/user/custom-ngrok-config.yml;/home/user/another-ngrok-config.
9597
```
9698
then generated ngrok command, should look like this:
9799
```bash
98-
/home/user/.ngrok2/ngrok http -config /home/user/custom-ngrok-config.yml 8080
100+
/home/user/.ngrok3/ngrok http -config /home/user/custom-ngrok-config.yml 8080
99101
# or for multiple configs, could be something like this:
100-
/home/user/.ngrok2/ngrok http -config /home/user/custom-ngrok-config.yml -config /home/user/another-ngrok-config.yml 8080
102+
/home/user/.ngrok3/ngrok http -config /home/user/custom-ngrok-config.yml -config /home/user/another-ngrok-config.yml 8080
101103
```
102104
###### configuration from Classpath
103105
If you prefer to keep ngrok configuration file inside your app, just add it as resource file and prefix `ngrok.config` property
@@ -112,39 +114,35 @@ example:
112114
```properties
113115
# to run default behavior
114116
ngrok.command=http 8080
115-
# should result with command = /home/user/.ngrok2/ngrok http 8000
117+
# should result with command = /home/user/.ngrok3/ngrok http 8000
116118
117119
# or some more specific
118120
ngrok.command=http -region=us -hostname=dev.example.com 8000
119-
# should be = /home/user/.ngrok2/ngrok http -region=us -hostname=dev.example.com 8000
121+
# should be = /home/user/.ngrok3/ngrok http -region=us -hostname=dev.example.com 8000
120122
```
121123
122124
##### Optional properties & descriptions
123125
```properties
126+
# if you want to use ngrok v2
127+
ngrok.legacy=true
128+
129+
# if you want to force download ngrok binary instead of using local one even if it's present in the PATH
130+
ngrok.useFromPath=false
131+
124132
# if you've got already running Ngrok instance somewhere else, you can specify its host & port, whoch defaults are:
125133
ngrok.host=http://127.0.0.1
126134
ngrok.port=4040
127135
128136
# if you want to use Ngrok directory location different than default, which are:
129-
# - for Windows C:\Users\user\.ngrok2
130-
# - for unix systems ~/.ngrok2
137+
# - for Windows C:\Users\user\.ngrok3
138+
# - for unix systems ~/.ngrok3
131139
# use ngrok.directory property, like below:
132140
ngrok.directory=C:\\Users\\user\\Desktop\\ngrok
133141
134142
# if for some reason Ngrok starting takes longer than really quick, you can override time
135143
# of waiting for ngrok startup:
136144
ngrok.waitForStartup.millis=3000
137145
138-
# if for some reason Ngrok binary file address has changed you can override it
139-
# by property corresponding to your OS
140-
ngrok.binary.windows32=https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-386.zip
141-
ngrok.binary.linux32=https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-386.zip
142-
ngrok.binary.osx32=https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-386.zip
143-
144-
ngrok.binary.windows=https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip
145-
ngrok.binary.linux=https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
146-
ngrok.binary.osx=https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip
147-
148146
# or just specify custom location as fallback:
149147
ngrok.binary.custom=http://not-exist.com/custom-ngrok-platform-bsd-arm-sth.zip
150148
```

pom.xml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@
7575

7676
<commons-io.version>2.11.0</commons-io.version>
7777
<commons-lang3.version>3.12.0</commons-lang3.version>
78-
<jackson-dataformat-yaml.version>2.13.2</jackson-dataformat-yaml.version>
78+
<commons-compress.version>1.21</commons-compress.version>
79+
<jackson-dataformat-yaml.version>2.13.4</jackson-dataformat-yaml.version>
7980
<vavr.version>0.10.4</vavr.version>
8081

81-
<spring-cloud-contract-wiremock.version>3.1.1</spring-cloud-contract-wiremock.version>
82+
<spring-cloud-contract-wiremock.version>3.1.4</spring-cloud-contract-wiremock.version>
8283
</properties>
8384

8485
<dependencies>
@@ -130,6 +131,12 @@
130131
<version>${commons-lang3.version}</version>
131132
</dependency>
132133

134+
<dependency>
135+
<groupId>org.apache.commons</groupId>
136+
<artifactId>commons-compress</artifactId>
137+
<version>${commons-compress.version}</version>
138+
</dependency>
139+
133140
<dependency>
134141
<groupId>org.springframework.boot</groupId>
135142
<artifactId>spring-boot-starter-test</artifactId>

src/main/java/ngrok/NgrokRunner.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@
1111
import ngrok.os.NgrokBinaryProvider;
1212
import ngrok.os.NgrokPlatformDetector;
1313
import ngrok.os.NgrokSystemCommandExecutor;
14-
import ngrok.util.NgrokDownloader;
14+
import ngrok.download.NgrokDownloader;
1515
import org.apache.commons.lang3.StringUtils;
1616
import org.springframework.boot.web.context.WebServerInitializedEvent;
1717
import org.springframework.context.ApplicationEventPublisher;
1818
import org.springframework.context.event.EventListener;
1919
import org.springframework.core.task.TaskExecutor;
2020

21-
import java.util.ArrayList;
22-
import java.util.Arrays;
2321
import java.util.List;
22+
import java.util.Objects;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
2425

2526
/**
2627
* For details see <a href="https://github.com/kilmajster/ngrok-spring-boot-starter">docs</a>.
@@ -62,9 +63,13 @@ public void run(WebServerInitializedEvent event) throws NgrokDownloadException,
6263
tunnels = ngrokApiClient.listTunnels(port);
6364
} else {
6465
NgrokTunnel httpsTunnel = ngrokApiClient.startTunnel(port, "http", applicationName + "-http-" + port);
65-
log.info("New Ngrok tunnel added -> [ {}: {} ]", httpsTunnel.getName(), httpsTunnel.getPublicUrl());
66+
if (Objects.nonNull(httpsTunnel)) {
67+
log.info("New Ngrok tunnel added -> [ {}: {} ]", httpsTunnel.getName(), httpsTunnel.getPublicUrl());
68+
}
6669
NgrokTunnel httpTunnel = ngrokApiClient.tunnelDetail(applicationName + "-http-" + port + " (http)");
67-
log.info("New Ngrok tunnel added -> [ {}: {} ]", httpTunnel.getName(), httpTunnel.getPublicUrl());
70+
if (Objects.nonNull(httpTunnel)) {
71+
log.info("New Ngrok tunnel added -> [ {}: {} ]", httpTunnel.getName(), httpTunnel.getPublicUrl());
72+
}
6873
tunnels = listOf(httpTunnel, httpsTunnel);
6974
}
7075
}
@@ -95,7 +100,7 @@ private void configureAuthToken() {
95100

96101
@SafeVarargs
97102
private static <T> List<T> listOf(T... args) {
98-
return new ArrayList<>(Arrays.asList(args));
103+
return Stream.of(args).filter(Objects::nonNull).collect(Collectors.toList());
99104
}
100105

101106
private void downloadAndExtractNgrokBinary() {

src/main/java/ngrok/configuration/NgrokAuthTokenUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ public class NgrokAuthTokenUtil {
2222
private static final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
2323

2424
public static Optional<String> getAuthToken(final String ngrokYamlConfigPath, final String ngrokDirectory) {
25-
final String defaultNgrokConfigFilePath = ngrokDirectory + File.separator + "ngrok.yml";
25+
final String defaultNgrokConfigFilePath = ngrokDirectory + File.separator + NgrokConfiguration.NGROK_CONFIG_FILE_NAME;
2626
boolean defaultConfigFileExist = Files.exists(Paths.get(defaultNgrokConfigFilePath));
2727
if (defaultConfigFileExist) {
2828
String authToken = readNgrokConfig(defaultNgrokConfigFilePath).getAuthToken();
2929
if (Objects.nonNull(authToken)) {
30-
return Optional.ofNullable(authToken);
30+
return Optional.of(authToken);
3131
}
3232
}
3333

@@ -44,7 +44,7 @@ private static NgrokYamlConfigModel readNgrokConfig(String path) {
4444

4545
@Data
4646
@NoArgsConstructor
47-
public static class NgrokYamlConfigModel {
47+
private static class NgrokYamlConfigModel {
4848
@JsonAlias("authtoken")
4949
private String authToken;
5050
}

src/main/java/ngrok/configuration/NgrokAutoConfiguration.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
import ngrok.NgrokComponent;
55
import ngrok.NgrokRunner;
66
import ngrok.api.NgrokApiClient;
7+
import ngrok.download.NgrokArchiveUrlProvider;
8+
import ngrok.download.NgrokLegacyV2ArchiveUrls;
9+
import ngrok.download.NgrokV3ArchiveUrls;
710
import ngrok.os.NgrokBinaryProvider;
811
import ngrok.os.NgrokPlatformDetector;
912
import ngrok.os.NgrokSystemCommandExecutor;
10-
import ngrok.util.NgrokDownloader;
13+
import ngrok.download.NgrokDownloader;
1114
import org.springframework.beans.factory.annotation.Qualifier;
1215
import org.springframework.beans.factory.annotation.Value;
1316
import org.springframework.context.ApplicationEventPublisher;
@@ -39,4 +42,9 @@ public NgrokRunner ngrokRunner(
3942
ngrokConfigurationProvider, ngrokDownloader, ngrokPlatformDetector, ngrokSystemCommandExecutor,
4043
ngrokExecutor, applicationName);
4144
}
45+
46+
@Bean
47+
public NgrokArchiveUrlProvider ngrokArchiveUrlProvider(NgrokConfiguration ngrokConfiguration) {
48+
return ngrokConfiguration.isLegacy() ? new NgrokLegacyV2ArchiveUrls() : new NgrokV3ArchiveUrls();
49+
}
4250
}

src/main/java/ngrok/configuration/NgrokConfiguration.java

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import lombok.NoArgsConstructor;
55
import lombok.Setter;
66
import ngrok.NgrokComponent;
7+
import ngrok.exception.NgrokMalformedConfigurationException;
78
import org.springframework.boot.context.properties.ConfigurationProperties;
89
import org.springframework.boot.context.properties.NestedConfigurationProperty;
10+
import org.springframework.context.annotation.Configuration;
11+
12+
import java.util.Objects;
913

1014
@Getter
1115
@Setter
@@ -16,12 +20,25 @@ public class NgrokConfiguration {
1620

1721
public static final String NGROK_ENABLED = "ngrok.enabled";
1822
public static final String NGROK_CONFIG_FILES_SEPARATOR = ";";
23+
public static final String NGROK_CONFIG_FILE_NAME = "ngrok.yml";
24+
public static final String NGROK_DIRECTORY_NAME = ".ngrok3";
25+
public static final String NGROK_LEGACY_DIRECTORY_NAME = ".ngrok2";
1926

2027
/**
2128
* Enable ngrok
2229
*/
2330
private Boolean enabled;
2431

32+
/**
33+
* If true, ngrok v2 will be used.
34+
*/
35+
private boolean legacy;
36+
37+
/**
38+
* If ngrok binary is present in PATH, use it.
39+
*/
40+
private boolean useFromPath = true;
41+
2542
/**
2643
* Property for personal Ngrok authToken, it can be found here - https://dashboard.ngrok.com/get-started/your-authtoken
2744
*/
@@ -59,51 +76,8 @@ public class NgrokConfiguration {
5976
private Long startupDelay = 3000L;
6077

6178
/**
62-
* Set the binary download URL
79+
* Url of custom Ngrok binary archive
6380
*/
64-
@NestedConfigurationProperty
65-
private NgrokBinary binary = new NgrokBinary();
66-
67-
@Getter
68-
@Setter
69-
@NoArgsConstructor
70-
public static class NgrokBinary {
71-
72-
/**
73-
* Windows 64 bit binary
74-
*/
75-
private String windows = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip";
76-
77-
/**
78-
* Linux 64 bit binary
79-
*/
80-
private String linux = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip";
81-
82-
/**
83-
* OSX 64 bit binary
84-
*/
85-
private String osx = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip";
86-
87-
/**
88-
* Windows 32 bit binary
89-
*/
90-
private String windows32 = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-386.zip";
91-
92-
/**
93-
* Linux 32 bit binary
94-
*/
95-
private String linux32 = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-386.zip";
96-
97-
/**
98-
* OSX 32 bit binary
99-
*/
100-
private String osx32 = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-386.zip";
101-
102-
/**
103-
* Custom url to download ngrok binary from (independent from system)
104-
*/
105-
private String custom;
106-
107-
}
81+
private String customArchiveUrl;
10882

10983
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package ngrok.download;
2+
3+
import ngrok.NgrokComponent;
4+
5+
@NgrokComponent
6+
public interface NgrokArchiveUrlProvider {
7+
8+
String getWindows();
9+
10+
String getLinux();
11+
12+
String getOsx();
13+
14+
String getWindows32();
15+
16+
String getLinux32();
17+
18+
String getOsx32();
19+
20+
}

src/main/java/ngrok/util/NgrokDownloader.java renamed to src/main/java/ngrok/download/NgrokDownloader.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package ngrok.util;
1+
package ngrok.download;
22

33
import lombok.RequiredArgsConstructor;
44
import lombok.extern.slf4j.Slf4j;
@@ -21,6 +21,7 @@
2121
@RequiredArgsConstructor
2222
public class NgrokDownloader {
2323

24+
private final NgrokArchiveUrlProvider ngrokArchiveUrlProvider;
2425
private final NgrokConfiguration ngrokConfiguration;
2526
private final NgrokPlatformDetector platformDetector;
2627

@@ -30,8 +31,8 @@ public void downloadAndExtractNgrokTo(String destinationPath) throws NgrokDownlo
3031
}
3132

3233
private String downloadNgrokTo(String destinationPath) throws NgrokDownloadException {
33-
String zipFileName = getFileNameFromUrl(getBinaryUrl());
34-
String destinationFile = FilenameUtils.concat(destinationPath, zipFileName);
34+
String archiveFileName = getFileNameFromUrl(getBinaryUrl());
35+
String destinationFile = FilenameUtils.concat(destinationPath, archiveFileName);
3536

3637
if (Files.exists(Paths.get(destinationFile))) {
3738
log.info("Skipping downloading, cached archive available at {}", destinationFile);
@@ -65,24 +66,21 @@ private String getFileNameFromUrl(String url) {
6566
return url.substring(getBinaryUrl().lastIndexOf("/") + 1);
6667
}
6768

68-
public String getBinaryUrl() {
69-
70-
final NgrokConfiguration.NgrokBinary ngrokBinary = ngrokConfiguration.getBinary();
71-
72-
if (StringUtils.isNotBlank(ngrokBinary.getCustom())) {
73-
return ngrokBinary.getCustom();
69+
private String getBinaryUrl() {
70+
if (StringUtils.isNotBlank(ngrokConfiguration.getCustomArchiveUrl())) {
71+
return ngrokConfiguration.getCustomArchiveUrl();
7472
}
7573

7674
if (platformDetector.isWindows()) {
77-
return platformDetector.is64bitOS() ? ngrokBinary.getWindows() : ngrokBinary.getWindows32();
75+
return platformDetector.is64bitOS() ? ngrokArchiveUrlProvider.getWindows() : ngrokArchiveUrlProvider.getWindows32();
7876
}
7977

8078
if (platformDetector.isMacOS()) {
81-
return platformDetector.is64bitOS() ? ngrokBinary.getOsx() : ngrokBinary.getOsx32();
79+
return platformDetector.is64bitOS() ? ngrokArchiveUrlProvider.getOsx() : ngrokArchiveUrlProvider.getOsx32();
8280
}
8381

8482
if (platformDetector.isLinux()) {
85-
return platformDetector.is64bitOS() ? ngrokBinary.getLinux() : ngrokBinary.getLinux32();
83+
return platformDetector.is64bitOS() ? ngrokArchiveUrlProvider.getLinux() : ngrokArchiveUrlProvider.getLinux32();
8684
}
8785

8886
throw new NgrokDownloadException("Unsupported OS");

0 commit comments

Comments
 (0)