All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.alexdlaird.ngrok.protocol.CreateTunnel Maven / Gradle / Ivy

There is a newer version: 2.3.1
Show newest version
/*
 * Copyright (c) 2021-2024 Alex Laird
 *
 * SPDX-License-Identifier: MIT
 */

package com.github.alexdlaird.ngrok.protocol;

import com.github.alexdlaird.http.HttpClient;
import com.github.alexdlaird.ngrok.NgrokClient;
import com.github.alexdlaird.ngrok.conf.JavaNgrokConfig;
import com.github.alexdlaird.ngrok.installer.NgrokVersion;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

/**
 * An object that represents a ngrok Tunnel creation request. This object can be serialized and passed to
 * the {@link HttpClient}.
 *
 * 

Basic Usage

*
 * final NgrokClient ngrokClient = new NgrokClient.Builder().build();
 *
 * final CreateTunnel createTunnel = new CreateTunnel.Builder()
 *         .withName("my-tunnel")
 *         .withProto(Proto.TCP)
 *         .withAddr(5000)
 *         .build();
 *
 * final HttpClient httpClient = new DefaultHttpClient.Builder().build()
 * final Response<SomePOJOResponse> postResponse = httpClient.post("http://localhost:4040/api/tunnels",
 *                                                                 createTunnel,
 *                                                                 Tunnel.class);
 * 
*

ngrok Version Compatibility

* java-ngrok is compatible with ngrok v2 and v3, but by default it will install v3. To * install v2 instead, set the version with {@link JavaNgrokConfig.Builder#withNgrokVersion(NgrokVersion)} and * {@link CreateTunnel.Builder#withNgrokVersion(NgrokVersion)}. */ public class CreateTunnel { // The ngrokVersion is transient so that it can be serialized to a valid request private final transient NgrokVersion ngrokVersion; private final String name; private final Proto proto; private final String domain; private final String addr; private final Boolean inspect; private final String auth; private final String hostHeader; private final BindTls bindTls; private final String subdomain; private final String hostname; private final String crt; private final String key; private final String clientCas; private final String remoteAddr; private final String metadata; private final List schemes; private final List basicAuth; private final TunnelOAuth oauth; private final Float circuitBreaker; private final Boolean compression; private final String mutualTlsCas; private final String proxyProto; private final Boolean websocketTcpConverter; private final String terminateAt; private final TunnelHeader requestHeader; private final TunnelHeader responseHeader; private final TunnelIPRestriction ipRestriction; private final TunnelVerifyWebhook verifyWebhook; private final TunnelUserAgentFilter userAgentFilter; private final TunnelPolicy policyInbound; private final TunnelPolicy policyOutbound; private final List labels; private CreateTunnel(final Builder builder) { this.ngrokVersion = builder.ngrokVersion; this.name = builder.name; this.proto = builder.proto; this.domain = builder.domain; this.addr = builder.addr; this.inspect = builder.inspect; this.auth = builder.auth; this.hostHeader = builder.hostHeader; this.bindTls = builder.bindTls; this.subdomain = builder.subdomain; this.hostname = builder.hostname; this.crt = builder.crt; this.key = builder.key; this.clientCas = builder.clientCas; this.remoteAddr = builder.remoteAddr; this.metadata = builder.metadata; this.schemes = builder.schemes; this.basicAuth = builder.basicAuth; this.oauth = builder.oauth; this.circuitBreaker = builder.circuitBreaker; this.compression = builder.compression; this.mutualTlsCas = builder.mutualTlsCas; this.proxyProto = builder.proxyProto; this.websocketTcpConverter = builder.websocketTcpConverter; this.terminateAt = builder.terminateAt; this.requestHeader = builder.requestHeader; this.responseHeader = builder.responseHeader; this.ipRestriction = builder.ipRestriction; this.verifyWebhook = builder.verifyWebhook; this.userAgentFilter = builder.userAgentFilter; this.policyInbound = builder.policyInbound; this.policyOutbound = builder.policyOutbound; this.labels = builder.labels; } /** * Get the version of ngrok for which the tunnel was created. */ public NgrokVersion getNgrokVersion() { return ngrokVersion; } /** * Get the name of the tunnel. */ public String getName() { return name; } /** * Get the tunnel protocol. */ public Proto getProto() { return proto; } /** * Get the tunnel domain. */ public String getDomain() { return domain; } /** * Get the local port to which the tunnel will forward traffic. */ public String getAddr() { return addr; } /** * Whether HTTP request inspection on tunnels is enabled. */ public Boolean isInspect() { return inspect; } /** * Get HTTP basic authentication credentials enforced on tunnel requests. */ public String getAuth() { return auth; } /** * Get the HTTP Host header. */ public String getHostHeader() { return hostHeader; } /** * Get ngrok's bind_tls value. */ public BindTls getBindTls() { return bindTls; } /** * Get the subdomain. */ public String getSubdomain() { return subdomain; } /** * Get the hostname. */ public String getHostname() { return hostname; } /** * Get the PEM TLS certificate path that will be used to terminate TLS traffic before forwarding locally. */ public String getCrt() { return crt; } /** * Get the PEM TLS private key path that will be used to terminate TLS traffic before forwarding locally. */ public String getKey() { return key; } /** * Get the PEM TLS certificate authority path that will be used to verify incoming TLS client connection * certificates. */ public String getClientCas() { return clientCas; } /** * Get the bound remote TCP port on the given address. */ public String getRemoteAddr() { return remoteAddr; } /** * Get the arbitrary user-defined metadata that will appear in the ngrok service API when listing tunnels. */ public String getMetadata() { return metadata; } /** * Get the schemes to be bound. */ public List getSchemes() { return schemes; } /** * Get the list of HTTP basic authentication credentials to enforce on tunneled requests. */ public List getBasicAuth() { return basicAuth; } /** * Get the OAuth settings to be setup on the tunnel. */ public TunnelOAuth getOauth() { return oauth; } /** * Get the circuit breaker trigger. */ public Float getCircuitBreaker() { return circuitBreaker; } /** * Whether compression is enabled on this tunnel. */ public Boolean isCompression() { return compression; } /** * Get the path to the TLS certificate authority to verify client certs. */ public String getMutualTlsCas() { return mutualTlsCas; } /** * Get the proxy proto. */ public String getProxyProto() { return proxyProto; } /** * Whether ingress connections are converted to TCP upstream. */ public Boolean isWebsocketTcpConverter() { return websocketTcpConverter; } /** * Get the termination point. */ public String getTerminateAt() { return terminateAt; } /** * Get the Headers to be added or removed from requests. */ public TunnelHeader getRequestHeader() { return requestHeader; } /** * Get the Headers to be added or removed from responses. */ public TunnelHeader getResponseHeader() { return responseHeader; } /** * Get the IP restrictions for the tunnel. */ public TunnelIPRestriction getIpRestriction() { return ipRestriction; } /** * Get the signature for webhooks. */ public TunnelVerifyWebhook getVerifyWebhook() { return verifyWebhook; } /** * Get the UserAgent filters. */ public TunnelUserAgentFilter getUserAgentFilter() { return userAgentFilter; } /** * Get the inbound policy. */ public TunnelPolicy getPolicyInbound() { return policyInbound; } /** * Get the outbound policy. */ public TunnelPolicy getPolicyOutbound() { return policyOutbound; } /** * Get the labels. Note that labels can only be provisioned from the ngrok config file and not the * Builder. */ public List getLabels() { return labels; } /** * Builder for a {@link CreateTunnel}, which can be used to construct a request that conforms to ngrok's tunnel definition. See docs for that class for example usage. */ public static class Builder { private boolean setDefaults = false; private NgrokVersion ngrokVersion; private String name; private Proto proto; private String domain; private String addr; private Boolean inspect; private BindTls bindTls; private String auth; private String hostHeader; private String subdomain; private String hostname; private String crt; private String key; private String clientCas; private String remoteAddr; private String metadata; private List schemes; private List basicAuth; private TunnelOAuth oauth; private Float circuitBreaker; private Boolean compression; private String mutualTlsCas; private String proxyProto; private Boolean websocketTcpConverter; private String terminateAt; private TunnelHeader requestHeader; private TunnelHeader responseHeader; private TunnelIPRestriction ipRestriction; private TunnelVerifyWebhook verifyWebhook; private TunnelUserAgentFilter userAgentFilter; private TunnelPolicy policyInbound; private TunnelPolicy policyOutbound; private List labels; /** * Use this constructor if default values should not be populated in required attributes when {@link #build()} * is called. * *

If required attributes are not set in the built {@link CreateTunnel}, default values will be used in * methods like {@link NgrokClient#connect(CreateTunnel)}. */ public Builder() { } /** * Use this constructor if default values should be populated in required attributes when {@link #build()} is * called. * * @param setDefaults true to populate defaults. */ public Builder(final boolean setDefaults) { this.setDefaults = setDefaults; } /** * Copy a {@link CreateTunnel} in to a new Builder. Using this constructor will also set default attributes when * {@link #build} is called. * * @param createTunnel The CreateTunnel to copy. */ public Builder(final CreateTunnel createTunnel) { this.setDefaults = true; this.ngrokVersion = createTunnel.ngrokVersion; this.name = createTunnel.name; this.proto = createTunnel.proto; this.domain = createTunnel.domain; this.addr = createTunnel.addr; this.inspect = createTunnel.inspect; this.bindTls = createTunnel.bindTls; this.auth = createTunnel.auth; this.hostHeader = createTunnel.hostHeader; this.subdomain = createTunnel.subdomain; this.hostname = createTunnel.hostname; this.crt = createTunnel.crt; this.key = createTunnel.key; this.clientCas = createTunnel.clientCas; this.remoteAddr = createTunnel.remoteAddr; this.metadata = createTunnel.metadata; this.schemes = createTunnel.schemes; this.basicAuth = createTunnel.basicAuth; this.oauth = createTunnel.oauth; this.circuitBreaker = createTunnel.circuitBreaker; this.compression = createTunnel.compression; this.mutualTlsCas = createTunnel.mutualTlsCas; this.proxyProto = createTunnel.proxyProto; this.websocketTcpConverter = createTunnel.websocketTcpConverter; this.terminateAt = createTunnel.terminateAt; this.requestHeader = createTunnel.requestHeader; this.responseHeader = createTunnel.responseHeader; this.ipRestriction = createTunnel.ipRestriction; this.verifyWebhook = createTunnel.verifyWebhook; this.userAgentFilter = createTunnel.userAgentFilter; this.policyInbound = createTunnel.policyInbound; this.policyOutbound = createTunnel.policyOutbound; } /** * The major version of ngrok for which the tunnel will be created. */ public Builder withNgrokVersion(final NgrokVersion ngrokVersion) { this.ngrokVersion = ngrokVersion; return this; } /** * A friendly name for the tunnel, or the name of a ngrok tunnel definition to be used. */ public Builder withName(final String name) { this.name = name; return this; } /** * The tunnel protocol, defaults to {@link Proto#HTTP}. */ public Builder withProto(final Proto proto) { this.proto = proto; return this; } /** * The tunnel domain. */ public Builder withDomain(final String domain) { this.domain = domain; return this; } /** * The local port to which the tunnel will forward traffic, or a * local directory or network address, defaults to "80". */ public Builder withAddr(final String addr) { this.addr = addr; return this; } /** * See {@link #withAddr(String)}. */ public Builder withAddr(final int addr) { return withAddr(String.valueOf(addr)); } /** * Disable HTTP request inspection on tunnels. */ public Builder withoutInspect() { this.inspect = false; return this; } /** * HTTP basic authentication credentials to enforce on tunneled requests. * * @throws IllegalArgumentException The argument was invalid. */ public Builder withAuth(final String auth) { if (nonNull(basicAuth)) { throw new IllegalArgumentException("Cannot set both 'auth' and 'basicAuth'."); } this.auth = auth; return this; } /** * Rewrite the HTTP Host header to this value, or preserve to leave it unchanged. */ public Builder withHostHeader(final String hostHeader) { this.hostHeader = hostHeader; return this; } /** * Bind an HTTPS ({@link BindTls#TRUE} or HTTP ({@link BindTls#FALSE}) endpoint, defaults to * {@link BindTls#BOTH}. * * @throws IllegalArgumentException The argument was invalid. */ public Builder withBindTls(final BindTls bindTls) { if (nonNull(schemes)) { throw new IllegalArgumentException("Cannot set both 'schemes' and 'bindTls'."); } this.bindTls = bindTls; return this; } /** * See {@link #withBindTls(BindTls)}. */ public Builder withBindTls(final boolean bindTls) { return withBindTls(BindTls.valueOf(String.valueOf(bindTls).toUpperCase())); } /** * Subdomain name to request. If unspecified, uses the tunnel name. */ public Builder withSubdomain(final String subdomain) { this.subdomain = subdomain; return this; } /** * Hostname to request (requires reserved name and DNS CNAME). */ public Builder withHostname(final String hostname) { this.hostname = hostname; return this; } /** * PEM TLS certificate at this path to terminate TLS traffic before forwarding locally. */ public Builder withCrt(final String crt) { this.crt = crt; return this; } /** * PEM TLS private key at this path to terminate TLS traffic before forwarding locally. */ public Builder withKey(final String key) { this.key = key; return this; } /** * PEM TLS certificate authority at this path will verify incoming TLS client connection certificates. */ public Builder withClientCas(final String clientCas) { this.clientCas = clientCas; return this; } /** * Bind the remote TCP port on the given address. */ public Builder withRemoteAddr(final String remoteAddr) { this.remoteAddr = remoteAddr; return this; } /** * Arbitrary user-defined metadata that will appear in the ngrok service API when listing tunnels. */ public Builder withMetadata(final String metadata) { this.metadata = metadata; return this; } /** * The schemes to be bound. * * @throws IllegalArgumentException The argument was invalid. */ public Builder withSchemes(final List schemes) { if (nonNull(bindTls)) { throw new IllegalArgumentException("Cannot set both 'schemes' and 'bindTls'."); } this.schemes = Collections.unmodifiableList(schemes); return this; } /** * List of HTTP basic authentication credentials to enforce on tunneled requests. * * @throws IllegalArgumentException The argument was invalid. */ public Builder withBasicAuth(final List basicAuth) { if (nonNull(auth)) { throw new IllegalArgumentException("Cannot set both 'auth' and 'basicAuth'."); } this.basicAuth = Collections.unmodifiableList(basicAuth); return this; } /** * Set of OAuth settings to enable OAuth authentication on the tunnel endpoint. */ public Builder withOAuth(final TunnelOAuth oauth) { this.oauth = oauth; return this; } /** * The circuit breaker trigger. */ public Builder withCircuitBreaker(final Float circuitBreaker) { this.circuitBreaker = circuitBreaker; return this; } /** * Whether compression is enabled on this tunnel. */ public Builder withCompression(final Boolean compression) { this.compression = compression; return this; } /** * The path to the TLS certificate authority to verify client certs. */ public Builder withMutualTlsCas(final String mutualTlsCas) { this.mutualTlsCas = mutualTlsCas; return this; } /** * The proxy proto. */ public Builder withProxyProto(final String proxyProto) { this.proxyProto = proxyProto; return this; } /** * Whether ingress connections are converted to TCP upstream. */ public Builder withWebsocketTcpConverter(final Boolean websocketTcpConverter) { this.websocketTcpConverter = websocketTcpConverter; return this; } /** * The termination point. */ public Builder withTerminateAt(final String terminateAt) { this.terminateAt = terminateAt; return this; } /** * The Headers to be added or removed from requests. */ public Builder withRequestHeader(final TunnelHeader requestHeader) { this.requestHeader = requestHeader; return this; } /** * The Headers to be added or removed from responses. */ public Builder withResponseHeader(final TunnelHeader responseHeader) { this.responseHeader = responseHeader; return this; } /** * The IP restrictions for the tunnel. */ public Builder withIpRestriction(final TunnelIPRestriction ipRestriction) { this.ipRestriction = ipRestriction; return this; } /** * The signature for webhooks. */ public Builder withVerifyWebhook(final TunnelVerifyWebhook verifyWebhook) { this.verifyWebhook = verifyWebhook; return this; } /** * The UserAgent filter for the tunnel. */ public Builder withUserAgentFilter(final TunnelUserAgentFilter userAgentFilter) { this.userAgentFilter = userAgentFilter; return this; } /** * The inbound policy for the tunnel. */ public Builder withPolicyInbound(final TunnelPolicy policyInbound) { this.policyInbound = policyInbound; return this; } /** * The outbound policy for the tunnel. */ public Builder withPolicyOutbound(final TunnelPolicy policyOutbound) { this.policyOutbound = policyOutbound; return this; } /** * Populate any null attributes (except for name) in this Builder with values from the * given tunnelDefinition. * * @param tunnelDefinition The map from which null attributes will be populated. * @throws IllegalArgumentException The argument was invalid. */ public Builder withTunnelDefinition(Map tunnelDefinition) { if (isNull(this.proto) && tunnelDefinition.containsKey("proto")) { this.proto = Proto.valueOf(((String) tunnelDefinition.get("proto")).toUpperCase()); } if (isNull(this.domain) && tunnelDefinition.containsKey("domain")) { this.domain = (String) tunnelDefinition.get("domain"); } if (isNull(this.addr) && tunnelDefinition.containsKey("addr")) { this.addr = (String) tunnelDefinition.get("addr"); } if (isNull(this.inspect) && tunnelDefinition.containsKey("inspect")) { this.inspect = Boolean.valueOf(String.valueOf(tunnelDefinition.get("inspect"))); } if (isNull(this.bindTls) && tunnelDefinition.containsKey("bind_tls")) { this.bindTls = BindTls.valueOf((String.valueOf(tunnelDefinition.get("bind_tls"))).toUpperCase()); } if (isNull(this.auth) && tunnelDefinition.containsKey("auth")) { this.auth = (String) tunnelDefinition.get("auth"); } if (isNull(this.hostHeader) && tunnelDefinition.containsKey("host_header")) { this.hostHeader = (String) tunnelDefinition.get("host_header"); } if (isNull(this.subdomain) && tunnelDefinition.containsKey("subdomain")) { this.subdomain = (String) tunnelDefinition.get("subdomain"); } if (isNull(this.hostname) && tunnelDefinition.containsKey("hostname")) { this.hostname = (String) tunnelDefinition.get("hostname"); } if (isNull(this.crt) && tunnelDefinition.containsKey("crt")) { this.crt = (String) tunnelDefinition.get("crt"); } if (isNull(this.key) && tunnelDefinition.containsKey("key")) { this.key = (String) tunnelDefinition.get("key"); } if (isNull(this.clientCas) && tunnelDefinition.containsKey("client_cas")) { this.clientCas = (String) tunnelDefinition.get("client_cas"); } if (isNull(this.remoteAddr) && tunnelDefinition.containsKey("remote_addr")) { this.remoteAddr = (String) tunnelDefinition.get("remote_addr"); } if (isNull(this.metadata) && tunnelDefinition.containsKey("metadata")) { this.metadata = (String) tunnelDefinition.get("metadata"); } if (isNull(this.schemes) && tunnelDefinition.containsKey("schemes")) { this.schemes = Collections.unmodifiableList( (List) tunnelDefinition.get("schemes") ); } if (isNull(this.basicAuth) && tunnelDefinition.containsKey("basic_auth")) { this.basicAuth = Collections.unmodifiableList( (List) tunnelDefinition.get("basic_auth") ); } if (isNull(this.oauth) && tunnelDefinition.containsKey("oauth")) { this.oauth = new TunnelOAuth.Builder((Map) tunnelDefinition.get("oauth")).build(); } if (isNull(this.circuitBreaker) && tunnelDefinition.containsKey("circuit_breaker")) { this.circuitBreaker = Float.valueOf(String.valueOf(tunnelDefinition.get("circuit_breaker"))); } if (isNull(this.compression) && tunnelDefinition.containsKey("compression")) { this.compression = Boolean.valueOf(String.valueOf(tunnelDefinition.get("compression"))); } if (isNull(this.mutualTlsCas) && tunnelDefinition.containsKey("mutual_tls_cas")) { this.mutualTlsCas = (String) tunnelDefinition.get("mutual_tls_cas"); } if (isNull(this.proxyProto) && tunnelDefinition.containsKey("proxy_proto")) { this.proxyProto = (String) tunnelDefinition.get("proxy_proto"); } if (isNull(this.websocketTcpConverter) && tunnelDefinition.containsKey("websocket_tcp_converter")) { this.websocketTcpConverter = Boolean.valueOf( String.valueOf(tunnelDefinition.get("websocket_tcp_converter")) ); } if (isNull(this.terminateAt) && tunnelDefinition.containsKey("terminate_at")) { this.terminateAt = (String) tunnelDefinition.get("terminate_at"); } if (isNull(this.requestHeader) && tunnelDefinition.containsKey("request_header")) { this.requestHeader = new TunnelHeader .Builder((Map) tunnelDefinition.get("request_header")) .build(); } if (isNull(this.responseHeader) && tunnelDefinition.containsKey("response_header")) { this.responseHeader = new TunnelHeader .Builder((Map) tunnelDefinition.get("response_header")) .build(); } if (isNull(this.ipRestriction) && tunnelDefinition.containsKey("ip_restriction")) { this.ipRestriction = new TunnelIPRestriction .Builder((Map) tunnelDefinition.get("ip_restriction")) .build(); } if (isNull(this.verifyWebhook) && tunnelDefinition.containsKey("verify_webhook")) { this.verifyWebhook = new TunnelVerifyWebhook .Builder((Map) tunnelDefinition.get("verify_webhook")) .build(); } if (isNull(this.userAgentFilter) && tunnelDefinition.containsKey("user_agent_filter")) { this.userAgentFilter = new TunnelUserAgentFilter .Builder((Map) tunnelDefinition.get("user_agent_filter")) .build(); } if (tunnelDefinition.containsKey("policy")) { final Map policy = (Map) tunnelDefinition.get("policy"); if (isNull(this.policyInbound) && policy.containsKey("inbound")) { this.policyInbound = new TunnelPolicy .Builder((Map) policy.get("inbound")) .build(); } if (isNull(this.policyOutbound) && policy.containsKey("outbound")) { this.policyOutbound = new TunnelPolicy .Builder((Map) policy.get("outbound")) .build(); } } if (tunnelDefinition.containsKey("labels")) { if (nonNull(bindTls)) { throw new IllegalArgumentException("'bindTls' cannot be set when 'labels' is also on the " + "tunnel definition."); } this.labels = Collections.unmodifiableList( (List) tunnelDefinition.get("labels") ); } // Returning this to allow chained configuration of // properties not visible in ngrok's GET /api/tunnels endpoint return this; } /** * Build the {@link CreateTunnel}. */ public CreateTunnel build() { if (isNull(ngrokVersion)) { ngrokVersion = NgrokVersion.V3; } if (setDefaults) { if (isNull(proto) && isNull(labels)) { proto = Proto.HTTP; } if (isNull(addr)) { addr = "80"; } if (isNull(name)) { if (!addr.startsWith("file://")) { name = String.format("%s-%s-%s", proto, addr, UUID.randomUUID()); } else { name = String.format("%s-file-%s", proto, UUID.randomUUID()); } } if (ngrokVersion == NgrokVersion.V2 && isNull(bindTls)) { bindTls = BindTls.BOTH; } if (ngrokVersion == NgrokVersion.V3) { if (nonNull(bindTls)) { if (bindTls == BindTls.TRUE) { schemes = List.of("https"); } else if (bindTls == BindTls.FALSE) { schemes = List.of("http"); } else { schemes = List.of("http", "https"); } bindTls = null; } if (nonNull(auth)) { basicAuth = List.of(auth); auth = null; } } } return new CreateTunnel(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy