diff --git a/ngrok-java-17/src/main/java/com/ngrok/net/TunnelServerSocket.java b/ngrok-java-17/src/main/java/com/ngrok/net/ListenerServerSocket.java similarity index 81% rename from ngrok-java-17/src/main/java/com/ngrok/net/TunnelServerSocket.java rename to ngrok-java-17/src/main/java/com/ngrok/net/ListenerServerSocket.java index a0e4fee..850425e 100644 --- a/ngrok-java-17/src/main/java/com/ngrok/net/TunnelServerSocket.java +++ b/ngrok-java-17/src/main/java/com/ngrok/net/ListenerServerSocket.java @@ -1,6 +1,6 @@ package com.ngrok.net; -import com.ngrok.Tunnel; +import com.ngrok.Listener; import java.io.IOException; import java.net.ServerSocket; @@ -10,15 +10,15 @@ /** * A server socket for accepting connections to an ngrok tunnel. */ -public class TunnelServerSocket extends ServerSocket { +public class ListenerServerSocket extends ServerSocket { /** * Creates a new server socket for the given tunnel. * * @param tunnel the tunnel to accept connections for * @throws IOException if an I/O error occurs */ - public TunnelServerSocket(Tunnel tunnel) throws IOException { - super(new TunnelSocketImpl(tunnel)); + public ListenerServerSocket(Listener listener) throws IOException { + super(new ListenerSocketImpl(listener)); } /** diff --git a/ngrok-java-17/src/main/java/com/ngrok/net/TunnelSocketImpl.java b/ngrok-java-17/src/main/java/com/ngrok/net/ListenerSocketImpl.java similarity index 72% rename from ngrok-java-17/src/main/java/com/ngrok/net/TunnelSocketImpl.java rename to ngrok-java-17/src/main/java/com/ngrok/net/ListenerSocketImpl.java index 19fabd7..66614ad 100644 --- a/ngrok-java-17/src/main/java/com/ngrok/net/TunnelSocketImpl.java +++ b/ngrok-java-17/src/main/java/com/ngrok/net/ListenerSocketImpl.java @@ -1,6 +1,6 @@ package com.ngrok.net; -import com.ngrok.Tunnel; +import com.ngrok.Listener; import java.io.IOException; import java.net.SocketImpl; @@ -9,16 +9,16 @@ * An implementation of the {@link AbstractSocketImpl} interface for * establishing a connection to an ngrok tunnel. */ -public class TunnelSocketImpl extends AbstractSocketImpl { - private final Tunnel tunnel; +public class ListenerSocketImpl extends AbstractSocketImpl { + private final Listener listener; /** * Creates a new tunnel socket implementation for the given tunnel. * * @param tunnel the tunnel to establish a connection to */ - public TunnelSocketImpl(Tunnel tunnel) { - this.tunnel = tunnel; + public ListenerSocketImpl(Listener listener) { + this.listener = listener; } /** @@ -30,6 +30,6 @@ public TunnelSocketImpl(Tunnel tunnel) { @Override protected void accept(SocketImpl s) throws IOException { var csi = (ConnectionSocketImpl) s; - csi.setConnection(tunnel.accept()); + csi.setConnection(listener.accept()); } } \ No newline at end of file diff --git a/ngrok-java-native/build.rs b/ngrok-java-native/build.rs index b456b67..2f70e40 100644 --- a/ngrok-java-native/build.rs +++ b/ngrok-java-native/build.rs @@ -18,9 +18,12 @@ fn main() -> Result<(), Box> { Cow::from("com.ngrok.NativeSession"), Cow::from("com.ngrok.NativeTcpListener"), Cow::from("com.ngrok.NativeTcpForwarder"), - Cow::from("com.ngrok.NativeTlsTunnel"), - Cow::from("com.ngrok.NativeHttpTunnel"), - Cow::from("com.ngrok.NativeLabeledTunnel"), + Cow::from("com.ngrok.NativeTlsListener"), + Cow::from("com.ngrok.NativeTlsForwarder"), + Cow::from("com.ngrok.NativeHttpListener"), + Cow::from("com.ngrok.NativeHttpForwarder"), + Cow::from("com.ngrok.NativeEdgeListener"), + Cow::from("com.ngrok.NativeEdgeForwarder"), Cow::from("com.ngrok.NativeEdgeConnection"), Cow::from("com.ngrok.NativeEndpointConnection"), ]; @@ -33,31 +36,20 @@ fn main() -> Result<(), Box> { Cow::from("com.ngrok.Session$RestartCallback"), Cow::from("com.ngrok.Session$UpdateCallback"), Cow::from("com.ngrok.Session$HeartbeatHandler"), - Cow::from("com.ngrok.Tunnel"), - Cow::from("com.ngrok.Tunnel$Builder"), - Cow::from("com.ngrok.EndpointTunnel"), - Cow::from("com.ngrok.EndpointTunnel$Builder"), - Cow::from("com.ngrok.TlsTunnel"), - Cow::from("com.ngrok.TlsTunnel$Builder"), - Cow::from("com.ngrok.HttpTunnel"), - Cow::from("com.ngrok.HttpTunnel$Header"), - Cow::from("com.ngrok.HttpTunnel$BasicAuthOptions"), - Cow::from("com.ngrok.HttpTunnel$OAuthOptions"), - Cow::from("com.ngrok.HttpTunnel$OIDCOptions"), - Cow::from("com.ngrok.HttpTunnel$WebhookVerification"), - Cow::from("com.ngrok.HttpTunnel$Builder"), - Cow::from("com.ngrok.LabeledTunnel"), - Cow::from("com.ngrok.LabeledTunnel$Label"), - Cow::from("com.ngrok.LabeledTunnel$Builder"), - Cow::from("com.ngrok.Connection"), - Cow::from("com.ngrok.EdgeConnection"), - Cow::from("com.ngrok.EndpointConnection"), - Cow::from("com.ngrok.TunnelInfo"), - Cow::from("com.ngrok.EndpointInfo"), - Cow::from("com.ngrok.Listener"), - Cow::from("com.ngrok.Forwarder"), - Cow::from("com.ngrok.Tcp"), - Cow::from("com.ngrok.Tcp$Builder"), + Cow::from("com.ngrok.MetadataBuilder"), + Cow::from("com.ngrok.EdgeBuilder"), + Cow::from("com.ngrok.EdgeBuilder$Label"), + Cow::from("com.ngrok.EndpointBuilder"), + Cow::from("com.ngrok.HttpBuilder"), + Cow::from("com.ngrok.Http$Header"), + Cow::from("com.ngrok.Http$BasicAuth"), + Cow::from("com.ngrok.Http$OAuth"), + Cow::from("com.ngrok.Http$OIDC"), + Cow::from("com.ngrok.Http$WebhookVerification"), + Cow::from("com.ngrok.TcpBuilder"), + Cow::from("com.ngrok.TlsBuilder"), + Cow::from("com.ngrok.AbstractEdge"), + Cow::from("com.ngrok.AbstractEndpoint"), ]; let output_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set")); diff --git a/ngrok-java-native/src/lib.rs b/ngrok-java-native/src/lib.rs index 77682c7..fbf6f42 100644 --- a/ngrok-java-native/src/lib.rs +++ b/ngrok-java-native/src/lib.rs @@ -1,19 +1,19 @@ use async_trait::async_trait; use bytes::Bytes; use com_ngrok::{ - ComNgrokEndpointInfo, ComNgrokHttpTunnelBuilder, ComNgrokHttpTunnelHeader, - ComNgrokLabeledTunnelBuilder, ComNgrokLabeledTunnelLabel, ComNgrokNativeEdgeConnection, - ComNgrokNativeEndpointConnection, ComNgrokNativeHttpTunnel, ComNgrokNativeLabeledTunnel, + ComNgrokEdgeBuilder, ComNgrokEdgeBuilderLabel, ComNgrokHttpBuilder, ComNgrokHttpHeader, + ComNgrokNativeEdgeConnection, ComNgrokNativeEdgeForwarder, ComNgrokNativeEdgeListener, + ComNgrokNativeEndpointConnection, ComNgrokNativeHttpForwarder, ComNgrokNativeHttpListener, ComNgrokNativeSession, ComNgrokNativeSessionClass, ComNgrokNativeTcpForwarder, - ComNgrokNativeTcpListener, ComNgrokNativeTlsTunnel, + ComNgrokNativeTcpListener, ComNgrokNativeTlsForwarder, ComNgrokNativeTlsListener, ComNgrokRuntimeLogger, ComNgrokSessionBuilder, ComNgrokSessionClientInfo, ComNgrokSessionHeartbeatHandler, ComNgrokSessionRestartCallback, ComNgrokSessionStopCallback, - ComNgrokSessionUpdateCallback, ComNgrokTlsTunnelBuilder, IOException, - IOExceptionErr, JavaUtilList, ComNgrokTcpBuilder, + ComNgrokSessionUpdateCallback, ComNgrokTcpBuilder, ComNgrokTlsBuilder, IOException, + IOExceptionErr, JavaNetUrl, JavaUtilList, JavaUtilMap, JavaUtilOptional, }; use futures::TryStreamExt; use once_cell::sync::OnceCell; -use std::{str::FromStr, sync::MutexGuard, time::Duration}; +use std::{collections::HashMap, str::FromStr, sync::MutexGuard, time::Duration}; use tokio::{io::AsyncReadExt, io::AsyncWriteExt, runtime::Runtime}; use tracing::{level_filters::LevelFilter, Level}; use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt}; @@ -28,13 +28,17 @@ use jaffi_support::{ }; use ngrok::{ - config::{OauthOptions, OidcOptions, ProxyProto, Scheme, TcpTunnelBuilder}, + config::{ + HttpTunnelBuilder, LabeledTunnelBuilder, OauthOptions, OidcOptions, ProxyProto, Scheme, + TcpTunnelBuilder, TlsTunnelBuilder, + }, conn::ConnInfo, forwarder::Forwarder, prelude::{EdgeConnInfo, EndpointConnInfo, ForwarderBuilder, TunnelBuilder}, session::{CommandHandler, HeartbeatHandler, Restart, Stop, Update}, tunnel::{ - EndpointInfo, HttpTunnel, LabeledTunnel, TcpTunnel, TlsTunnel, TunnelCloser, TunnelInfo, + EdgeInfo, EndpointInfo, HttpTunnel, LabeledTunnel, TcpTunnel, TlsTunnel, TunnelCloser, + TunnelInfo, }, EdgeConn, EndpointConn, Session, }; @@ -202,6 +206,40 @@ impl<'local> JavaUtilList<'local> { } } +impl<'local> JavaNetUrl<'local> { + fn as_string(self, env: JNIEnv<'local>) -> String { + env.call_method(self, "toString", "()Ljava/lang/String;", &[]) + .and_then(|o| o.l()) + .map(JString::from) + .and_then(|o| env.get_string(o)) + .expect("could not convert url to string") + .into() + } +} + +impl<'local> JavaUtilOptional<'local> { + fn of_string(self, env: JNIEnv<'local>) -> Option { + if self.is_present(env) { + let s: String = env + .call_method(self, "get", "()Ljava/lang/Object;", &[]) + .and_then(|o| o.l()) + .map(JString::from) + .and_then(|o| env.get_string(o)) + .expect("could not convert url to string") + .into(); + Some(s) + } else { + None + } + } + + fn is_present(self, env: JNIEnv<'local>) -> bool { + env.call_method(self, "isPresent", "()Z", &[]) + .and_then(|o| o.z()) + .expect("could not call isPresent") + } +} + fn io_exc(e: E) -> Error { Error::new(IOExceptionErr::IOException(IOException), e.to_string()) } @@ -325,23 +363,23 @@ struct NativeSessionRsImpl<'local> { } impl<'local> NativeSessionRsImpl<'local> { - fn build_tcp( + fn tcp_builder( &self, sess: MutexGuard, jttb: ComNgrokTcpBuilder<'local>, ) -> TcpTunnelBuilder { let mut bldr = sess.tcp_endpoint(); - let jatb = jttb.as_com_ngrok_endpoint_tunnel_builder(); - let jtb = jatb.as_com_ngrok_tunnel_builder(); + let jeb = jttb.as_com_ngrok_endpoint_builder(); + let jmb = jeb.as_com_ngrok_metadata_builder(); // from Tunnel.Builder - if jtb.has_metadata(self.env) { - bldr.metadata(jtb.get_metadata(self.env)); + if let Some(metadata) = jmb.get_metadata(self.env).of_string(self.env) { + bldr.metadata(metadata); } // from EndpointTunnel.Builder - let allow_cidr = jatb.get_allow_cidr(self.env); + let allow_cidr = jeb.get_allow_cidr(self.env); for i in 0..allow_cidr.size(self.env) { let cidr: JString = allow_cidr.get(self.env, i).into(); if let Some(cidr) = self.as_string(cidr) { @@ -349,7 +387,7 @@ impl<'local> NativeSessionRsImpl<'local> { } } - let deny_cidr = jatb.get_deny_cidr(self.env); + let deny_cidr = jeb.get_deny_cidr(self.env); for i in 0..deny_cidr.size(self.env) { let cidr: JString = deny_cidr.get(self.env, i).into(); if let Some(cidr) = self.as_string(cidr) { @@ -357,209 +395,37 @@ impl<'local> NativeSessionRsImpl<'local> { } } - if jatb.has_proxy_proto(self.env) { - bldr.proxy_proto(ProxyProto::from(jatb.get_proxy_proto_version(self.env))); - } + bldr.proxy_proto(ProxyProto::from(jeb.get_proxy_proto_version(self.env))); - if jatb.has_forwards_to(self.env) { - bldr.forwards_to(jatb.get_forwards_to(self.env)); + if let Some(forwards_to) = jeb.get_forwards_to(self.env).of_string(self.env) { + bldr.forwards_to(forwards_to); } // from TcpTunnel.Builder - if jttb.has_remote_address(self.env) { - bldr.remote_addr(jttb.get_remote_address(self.env)); + if let Some(remote_addr) = jttb.get_remote_address(self.env).of_string(self.env) { + bldr.remote_addr(remote_addr); } bldr } - fn endpoint_info(&self, tun: &T) -> ComNgrokEndpointInfo<'local> { - ComNgrokEndpointInfo::new_1com_ngrok_endpoint_info( - self.env, - tun.id().into(), - tun.forwards_to().into(), - tun.metadata().into(), - tun.proto().into(), - tun.url().into(), - ) - } -} - -impl<'local> JNIExt<'local> for NativeSessionRsImpl<'local> { - fn get_env(&self) -> &JNIEnv<'local> { - &self.env - } -} - -impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> { - fn from_env(env: JNIEnv<'local>) -> Self { - Self { env } - } - - fn connect_native( - &self, - _class: ComNgrokNativeSessionClass<'local>, - jsb: ComNgrokSessionBuilder<'local>, - ) -> Result, Error> { - let rt = RT.get().expect("runtime not initialized"); - - let mut bldr = Session::builder(); - - if jsb.has_authtoken(self.env) { - bldr.authtoken(jsb.get_authtoken(self.env)); - } - - if jsb.has_heartbeat_interval(self.env) { - let d: u64 = jsb.get_heartbeat_interval_ms(self.env).try_into().unwrap(); - bldr.heartbeat_interval(Duration::from_millis(d)) - .expect("invalid heartbeat interval"); - } - - if jsb.has_heartbeat_tolerance(self.env) { - let d: u64 = jsb.get_heartbeat_tolerance_ms(self.env).try_into().unwrap(); - bldr.heartbeat_tolerance(Duration::from_millis(d)) - .expect("invalid heartbeat tolerance"); - } - - let mut session_metadata = String::from(""); - if jsb.has_metadata(self.env) { - session_metadata = jsb.get_metadata(self.env); - bldr.metadata(session_metadata.clone()); - } - - if jsb.has_server_addr(self.env) { - bldr.server_addr(jsb.get_server_addr(self.env)) - .expect("invalid server address"); - } - - let ca_cert = jsb.get_ca_cert(self.env); - if !ca_cert.is_null() { - let ca_cert_data = ca_cert - .as_slice(&self.env) - .expect("cannot get ca cert data"); - bldr.ca_cert(Bytes::copy_from_slice(&ca_cert_data)); - } - - // TODO: tls_config - // TODO: connector? - - let stop_obj = jsb.stop_callback(self.env); - if !stop_obj.is_null() { - bldr.handle_stop_command(StopCallback::from(self.env, stop_obj)); - } - - let restart_obj = jsb.restart_callback(self.env); - if !restart_obj.is_null() { - bldr.handle_restart_command(RestartCallback::from(self.env, restart_obj)); - } - - let update_obj = jsb.update_callback(self.env); - if !update_obj.is_null() { - bldr.handle_update_command(UpdateCallback::from(self.env, update_obj)); - } - - let heartbeat_obj = jsb.heartbeat_handler(self.env); - if !heartbeat_obj.is_null() { - bldr.handle_heartbeat(HeartbeatCallback::from(self.env, heartbeat_obj)); - } - - let client_infos = jsb.get_client_infos(self.env); - for i in 0..client_infos.size(self.env) { - let client_info: ComNgrokSessionClientInfo = client_infos.get(self.env, i).into(); - let comments = if client_info.has_comments(self.env) { - Option::::Some(client_info.get_comments(self.env)) - } else { - Option::::None - }; - bldr.client_info( - client_info.get_type(self.env), - client_info.get_version(self.env), - comments, - ); - } - - match rt.block_on(bldr.connect()) { - Ok(sess) => { - let jsess = ComNgrokNativeSession::new_1com_ngrok_native_session( - self.env, - session_metadata, - ); - self.set_native(jsess, sess); - Ok(jsess) - } - Err(err) => io_exc_err(err), - } - } - - fn listen_tcp( - &self, - this: ComNgrokNativeSession<'local>, - jttb: ComNgrokTcpBuilder<'local>, - ) -> Result, Error> { - let rt = RT.get().expect("runtime not initialized"); - - let sess: MutexGuard = self.get_native(this); - let bldr = self.build_tcp(sess, jttb); - - let tun = rt.block_on(bldr.listen()); - match tun { - Ok(tun) => { - let jlistener = ComNgrokNativeTcpListener::new_1com_ngrok_native_tcp_listener( - self.env, self.endpoint_info(&tun), - ); - self.set_native(jlistener, tun); - Ok(jlistener) - } - Err(err) => io_exc_err(err), - } - } - - fn forward_tcp( - &self, - this: ComNgrokNativeSession<'local>, - jttb: ComNgrokTcpBuilder<'local>, - jurl: String, - ) -> Result, Error> { - let rt = RT.get().expect("runtime not initialized"); - - let sess: MutexGuard = self.get_native(this); - let bldr = self.build_tcp(sess, jttb); - - let url = Url::parse(jurl.as_str()).expect("cannot parse url"); - - let tun = rt.block_on(bldr.listen_and_forward(url)); - match tun { - Ok(tun) => { - let jforwarder = ComNgrokNativeTcpForwarder::new_1com_ngrok_native_tcp_forwarder( - self.env, self.endpoint_info(&tun), - ); - self.set_native(jforwarder, tun); - Ok(jforwarder) - } - Err(err) => io_exc_err(err), - } - } - - fn tls_tunnel( + fn tls_builder( &self, - this: ComNgrokNativeSession<'local>, - jttb: ComNgrokTlsTunnelBuilder<'local>, - ) -> Result, Error> { - let rt = RT.get().expect("runtime not initialized"); - - let sess: MutexGuard = self.get_native(this); + sess: MutexGuard, + jttb: ComNgrokTlsBuilder<'local>, + ) -> TlsTunnelBuilder { let mut bldr = sess.tls_endpoint(); - let jatb = jttb.as_com_ngrok_endpoint_tunnel_builder(); - let jtb = jatb.as_com_ngrok_tunnel_builder(); + let jeb = jttb.as_com_ngrok_endpoint_builder(); + let jmb = jeb.as_com_ngrok_metadata_builder(); // from Tunnel.Builder - if jtb.has_metadata(self.env) { - bldr.metadata(jtb.get_metadata(self.env)); + if let Some(metadata) = jmb.get_metadata(self.env).of_string(self.env) { + bldr.metadata(metadata); } // from EndpointTunnel.Builder - let allow_cidr = jatb.get_allow_cidr(self.env); + let allow_cidr = jeb.get_allow_cidr(self.env); for i in 0..allow_cidr.size(self.env) { let cidr: JString = allow_cidr.get(self.env, i).into(); if let Some(cidr) = self.as_string(cidr) { @@ -567,7 +433,7 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> } } - let deny_cidr = jatb.get_deny_cidr(self.env); + let deny_cidr = jeb.get_deny_cidr(self.env); for i in 0..deny_cidr.size(self.env) { let cidr: JString = deny_cidr.get(self.env, i).into(); if let Some(cidr) = self.as_string(cidr) { @@ -575,17 +441,15 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> } } - if jatb.has_proxy_proto(self.env) { - bldr.proxy_proto(ProxyProto::from(jatb.get_proxy_proto_version(self.env))); - } + bldr.proxy_proto(ProxyProto::from(jeb.get_proxy_proto_version(self.env))); - if jatb.has_forwards_to(self.env) { - bldr.forwards_to(jatb.get_forwards_to(self.env)); + if let Some(forwards_to) = jeb.get_forwards_to(self.env).of_string(self.env) { + bldr.forwards_to(forwards_to); } // from TlsTunnel.Builder - if jttb.has_domain(self.env) { - bldr.domain(jttb.get_domain(self.env)); + if let Some(domain) = jttb.get_domain(self.env).of_string(self.env) { + bldr.domain(domain); } let mtls = jttb.get_mutual_tlsca(self.env); @@ -607,46 +471,29 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> ); } (cert, key) if cert.is_null() && key.is_null() => {} - _ => return io_exc_err("requires both terminationCertPEM and terminationKeyPEM"), + _ => panic!("requires both terminationCertPEM and terminationKeyPEM"), } - match rt.block_on(bldr.listen()) { - Ok(tun) => { - let jtunnel = ComNgrokNativeTlsTunnel::new_1com_ngrok_native_tls_tunnel( - self.env, - tun.id().into(), - tun.forwards_to().into(), - tun.metadata().into(), - "tls".into(), - tun.url().into(), - ); - self.set_native(jtunnel, tun); - Ok(jtunnel) - } - Err(err) => io_exc_err(err), - } + bldr } - fn http_tunnel( + fn http_builder( &self, - this: ComNgrokNativeSession<'local>, - jhtb: ComNgrokHttpTunnelBuilder<'local>, - ) -> Result, Error> { - let rt = RT.get().expect("runtime not initialized"); - - let sess: MutexGuard = self.get_native(this); + sess: MutexGuard, + jhtb: ComNgrokHttpBuilder<'local>, + ) -> HttpTunnelBuilder { let mut bldr = sess.http_endpoint(); - let jatb = jhtb.as_com_ngrok_endpoint_tunnel_builder(); - let jtb = jatb.as_com_ngrok_tunnel_builder(); + let jeb = jhtb.as_com_ngrok_endpoint_builder(); + let jmb = jeb.as_com_ngrok_metadata_builder(); // from Tunnel.Builder - if jtb.has_metadata(self.env) { - bldr.metadata(jtb.get_metadata(self.env)); + if let Some(metadata) = jmb.get_metadata(self.env).of_string(self.env) { + bldr.metadata(metadata); } // from EndpointTunnel.Builder - let allow_cidr = jatb.get_allow_cidr(self.env); + let allow_cidr = jeb.get_allow_cidr(self.env); for i in 0..allow_cidr.size(self.env) { let cidr: JString = allow_cidr.get(self.env, i).into(); if let Some(cidr) = self.as_string(cidr) { @@ -654,7 +501,7 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> } } - let deny_cidr = jatb.get_deny_cidr(self.env); + let deny_cidr = jeb.get_deny_cidr(self.env); for i in 0..deny_cidr.size(self.env) { let cidr: JString = deny_cidr.get(self.env, i).into(); if let Some(cidr) = self.as_string(cidr) { @@ -662,12 +509,10 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> } } - if jatb.has_proxy_proto(self.env) { - bldr.proxy_proto(ProxyProto::from(jatb.get_proxy_proto_version(self.env))); - } + bldr.proxy_proto(ProxyProto::from(jeb.get_proxy_proto_version(self.env))); - if jatb.has_forwards_to(self.env) { - bldr.forwards_to(jatb.get_forwards_to(self.env)); + if let Some(forwards_to) = jeb.get_forwards_to(self.env).of_string(self.env) { + bldr.forwards_to(forwards_to); } // from HttpTunnel.Builder @@ -701,13 +546,13 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> let request_headers = jhtb.get_request_headers(self.env); for i in 0..request_headers.size(self.env) { - let header: ComNgrokHttpTunnelHeader = request_headers.get(self.env, i).into(); + let header: ComNgrokHttpHeader = request_headers.get(self.env, i).into(); bldr.request_header(header.get_name(self.env), header.get_value(self.env)); } let response_headers = jhtb.get_response_headers(self.env); for i in 0..response_headers.size(self.env) { - let header: ComNgrokHttpTunnelHeader = response_headers.get(self.env, i).into(); + let header: ComNgrokHttpHeader = response_headers.get(self.env, i).into(); bldr.response_header(header.get_name(self.env), header.get_value(self.env)); } @@ -727,7 +572,7 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> } } - let basic_auth = jhtb.get_basic_auth_options(self.env); + let basic_auth = jhtb.get_basic_auth(self.env); if !basic_auth.is_null() { bldr.basic_auth( basic_auth.get_username(self.env), @@ -735,7 +580,7 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> ); } - let joauth = jhtb.get_oauth_options(self.env); + let joauth = jhtb.get_oauth(self.env); if !joauth.is_null() { let mut oauth = OauthOptions::new(joauth.get_provider(self.env)); if joauth.has_client_id(self.env) { @@ -754,7 +599,7 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> bldr.oauth(oauth); } - let joidc = jhtb.get_oidc_options(self.env); + let joidc = jhtb.get_oidc(self.env); if !joidc.is_null() { let mut oidc = OidcOptions::new( joidc.get_issuer_url(self.env), @@ -778,60 +623,43 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> bldr.webhook_verification(jwv.get_provider(self.env), jwv.get_secret(self.env)); } - match rt.block_on(bldr.listen()) { - Ok(tun) => { - let jtunnel = ComNgrokNativeHttpTunnel::new_1com_ngrok_native_http_tunnel( - self.env, - tun.id().into(), - tun.forwards_to().into(), - tun.metadata().into(), - "http".into(), - tun.url().into(), - ); - self.set_native(jtunnel, tun); - Ok(jtunnel) - } - Err(err) => io_exc_err(err), - } + bldr } - fn labeled_tunnel( + fn edge_builder( &self, - this: ComNgrokNativeSession<'local>, - jltb: ComNgrokLabeledTunnelBuilder<'local>, - ) -> Result, Error> { - let rt = RT.get().expect("runtime not initialized"); - - let sess: MutexGuard = self.get_native(this); + sess: MutexGuard, + jltb: ComNgrokEdgeBuilder<'local>, + ) -> LabeledTunnelBuilder { let mut bldr = sess.labeled_tunnel(); - let jtb = jltb.as_com_ngrok_tunnel_builder(); + let jmb = jltb.as_com_ngrok_metadata_builder(); // from Tunnel.Builder - if jtb.has_metadata(self.env) { - bldr.metadata(jtb.get_metadata(self.env)); + if let Some(metadata) = jmb.get_metadata(self.env).of_string(self.env) { + bldr.metadata(metadata); } // from LabeledTunnel.Builder let labels = jltb.get_labels(self.env); for i in 0..labels.size(self.env) { - let label: ComNgrokLabeledTunnelLabel = labels.get(self.env, i).into(); + let label: ComNgrokEdgeBuilderLabel = labels.get(self.env, i).into(); bldr.label(label.get_name(self.env), label.get_value(self.env)); } - match rt.block_on(bldr.listen()) { - Ok(tun) => { - let jtunnel = ComNgrokNativeLabeledTunnel::new_1com_ngrok_native_labeled_tunnel( - self.env, - tun.id().into(), - tun.forwards_to().into(), - tun.metadata().into(), - ); - self.set_native(jtunnel, tun); - Ok(jtunnel) - } - Err(err) => io_exc_err(err), + bldr + } + + fn labels_map(&self, labels: &HashMap) -> JavaUtilMap<'local> { + let map_class = self.env.find_class("java/util/HashMap").expect("msg"); + let map = self.env.new_object(map_class, "()", &[]).expect("msg"); + let mmap = self.env.get_map(map).expect("msg"); + for (key, value) in labels { + let jkey = self.env.new_string(key).expect("msg"); + let jvalue = self.env.new_string(value).expect("msg"); + mmap.put(jkey.into(), jvalue.into()).expect("xxx"); } + map.into() } fn close_tunnel( @@ -844,60 +672,415 @@ impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> let sess: MutexGuard = self.get_native(this); rt.block_on(sess.close_tunnel(tunnel_id)).map_err(io_exc) } - - fn close( - &self, - this: ComNgrokNativeSession<'local>, - ) -> Result<(), jaffi_support::Error> { - let rt = RT.get().expect("runtime not initialized"); - - let mut sess: Session = self.take_native(this); - rt.block_on(sess.close()).map_err(io_exc) - } -} - -struct NativeTcpListenerRsImpl<'local> { - env: JNIEnv<'local>, } -impl<'local> JNIExt<'local> for NativeTcpListenerRsImpl<'local> { +impl<'local> JNIExt<'local> for NativeSessionRsImpl<'local> { fn get_env(&self) -> &JNIEnv<'local> { &self.env } } -impl<'local> com_ngrok::NativeTcpListenerRs<'local> for NativeTcpListenerRsImpl<'local> { +impl<'local> com_ngrok::NativeSessionRs<'local> for NativeSessionRsImpl<'local> { fn from_env(env: JNIEnv<'local>) -> Self { Self { env } } - fn accept( + fn connect_native( &self, - this: ComNgrokNativeTcpListener<'local>, - ) -> Result, Error> { + _class: ComNgrokNativeSessionClass<'local>, + jsb: ComNgrokSessionBuilder<'local>, + ) -> Result, Error> { let rt = RT.get().expect("runtime not initialized"); - let mut tun: MutexGuard = self.get_native(this); - match rt.block_on(tun.try_next()) { - Ok(Some(conn)) => { - let jconn: ComNgrokNativeEndpointConnection<'_> = - ComNgrokNativeEndpointConnection::new_1com_ngrok_native_endpoint_connection( - self.env, - conn.remote_addr().to_string(), - conn.proto().to_string(), - ); - self.set_native(jconn, conn); - Ok(jconn) - } - Ok(None) => io_exc_err("could not get next conn"), - Err(err) => io_exc_err(err), - } - } + let mut bldr = Session::builder(); - fn close(&self, this: ComNgrokNativeTcpListener<'local>) -> Result<(), Error> { - let rt = RT.get().expect("runtime not initialized"); + bldr.authtoken(jsb.get_authtoken(self.env)); - let mut tun: TcpTunnel = self.take_native(this); + if jsb.has_heartbeat_interval(self.env) { + let d: u64 = jsb.get_heartbeat_interval_ms(self.env).try_into().unwrap(); + bldr.heartbeat_interval(Duration::from_millis(d)) + .expect("invalid heartbeat interval"); + } + + if jsb.has_heartbeat_tolerance(self.env) { + let d: u64 = jsb.get_heartbeat_tolerance_ms(self.env).try_into().unwrap(); + bldr.heartbeat_tolerance(Duration::from_millis(d)) + .expect("invalid heartbeat tolerance"); + } + + let mut session_metadata = String::from(""); + if let Some(metadata) = jsb.get_metadata(self.env).of_string(self.env) { + session_metadata = metadata.clone(); + bldr.metadata(metadata); + } + + if jsb.has_server_addr(self.env) { + bldr.server_addr(jsb.get_server_addr(self.env)) + .expect("invalid server address"); + } + + let ca_cert = jsb.get_ca_cert(self.env); + if !ca_cert.is_null() { + let ca_cert_data = ca_cert + .as_slice(&self.env) + .expect("cannot get ca cert data"); + bldr.ca_cert(Bytes::copy_from_slice(&ca_cert_data)); + } + + // TODO: tls_config + // TODO: connector? + + let stop_obj = jsb.stop_callback(self.env); + if !stop_obj.is_null() { + bldr.handle_stop_command(StopCallback::from(self.env, stop_obj)); + } + + let restart_obj = jsb.restart_callback(self.env); + if !restart_obj.is_null() { + bldr.handle_restart_command(RestartCallback::from(self.env, restart_obj)); + } + + let update_obj = jsb.update_callback(self.env); + if !update_obj.is_null() { + bldr.handle_update_command(UpdateCallback::from(self.env, update_obj)); + } + + let heartbeat_obj = jsb.heartbeat_handler(self.env); + if !heartbeat_obj.is_null() { + bldr.handle_heartbeat(HeartbeatCallback::from(self.env, heartbeat_obj)); + } + + let client_infos = jsb.get_client_infos(self.env); + for i in 0..client_infos.size(self.env) { + let client_info: ComNgrokSessionClientInfo = client_infos.get(self.env, i).into(); + let comments = if client_info.has_comments(self.env) { + Option::::Some(client_info.get_comments(self.env)) + } else { + Option::::None + }; + bldr.client_info( + client_info.get_type(self.env), + client_info.get_version(self.env), + comments, + ); + } + + match rt.block_on(bldr.connect()) { + Ok(sess) => { + let jsess = ComNgrokNativeSession::new_1com_ngrok_native_session( + self.env, + session_metadata, + ); + self.set_native(jsess, sess); + Ok(jsess) + } + Err(err) => io_exc_err(err), + } + } + + fn listen_tcp( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokTcpBuilder<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.tcp_builder(sess, jttb); + + let tun = rt.block_on(bldr.listen()); + match tun { + Ok(tun) => { + let jlistener = ComNgrokNativeTcpListener::new_1com_ngrok_native_tcp_listener( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + tun.proto().to_string(), + tun.url().into(), + ); + self.set_native(jlistener, tun); + Ok(jlistener) + } + Err(err) => io_exc_err(err), + } + } + + fn forward_tcp( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokTcpBuilder<'local>, + jurl: com_ngrok::JavaNetUrl<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.tcp_builder(sess, jttb); + + let url = Url::parse(jurl.as_string(self.env).as_str()).expect("cannot parse url"); + + let tun = rt.block_on(bldr.listen_and_forward(url)); + match tun { + Ok(tun) => { + let jforwarder = ComNgrokNativeTcpForwarder::new_1com_ngrok_native_tcp_forwarder( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + tun.proto().to_string(), + tun.url().into(), + ); + self.set_native(jforwarder, tun); + Ok(jforwarder) + } + Err(err) => io_exc_err(err), + } + } + + fn listen_tls( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokTlsBuilder<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.tls_builder(sess, jttb); + + let tun = rt.block_on(bldr.listen()); + match tun { + Ok(tun) => { + let jlistener = ComNgrokNativeTlsListener::new_1com_ngrok_native_tls_listener( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + tun.proto().to_string(), + tun.url().into(), + ); + self.set_native(jlistener, tun); + Ok(jlistener) + } + Err(err) => io_exc_err(err), + } + } + + fn forward_tls( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokTlsBuilder<'local>, + jurl: com_ngrok::JavaNetUrl<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.tls_builder(sess, jttb); + + let url = Url::parse(jurl.as_string(self.env).as_str()).expect("cannot parse url"); + + let tun = rt.block_on(bldr.listen_and_forward(url)); + match tun { + Ok(tun) => { + let jforwarder = ComNgrokNativeTlsForwarder::new_1com_ngrok_native_tls_forwarder( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + tun.proto().to_string(), + tun.url().into(), + ); + self.set_native(jforwarder, tun); + Ok(jforwarder) + } + Err(err) => io_exc_err(err), + } + } + + fn listen_http( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokHttpBuilder<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.http_builder(sess, jttb); + + let tun = rt.block_on(bldr.listen()); + match tun { + Ok(tun) => { + let jlistener = ComNgrokNativeHttpListener::new_1com_ngrok_native_http_listener( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + tun.proto().to_string(), + tun.url().into(), + ); + self.set_native(jlistener, tun); + Ok(jlistener) + } + Err(err) => io_exc_err(err), + } + } + + fn forward_http( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokHttpBuilder<'local>, + jurl: com_ngrok::JavaNetUrl<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.http_builder(sess, jttb); + + let url = Url::parse(jurl.as_string(self.env).as_str()).expect("cannot parse url"); + + let tun = rt.block_on(bldr.listen_and_forward(url)); + match tun { + Ok(tun) => { + let jforwarder = ComNgrokNativeHttpForwarder::new_1com_ngrok_native_http_forwarder( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + tun.proto().to_string(), + tun.url().into(), + ); + self.set_native(jforwarder, tun); + Ok(jforwarder) + } + Err(err) => io_exc_err(err), + } + } + + fn listen_edge( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokEdgeBuilder<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.edge_builder(sess, jttb); + + let tun = rt.block_on(bldr.listen()); + match tun { + Ok(tun) => { + let jlistener = ComNgrokNativeEdgeListener::new_1com_ngrok_native_edge_listener( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + self.labels_map(tun.labels()), + ); + self.set_native(jlistener, tun); + Ok(jlistener) + } + Err(err) => io_exc_err(err), + } + } + + fn forward_edge( + &self, + this: ComNgrokNativeSession<'local>, + jttb: ComNgrokEdgeBuilder<'local>, + jurl: com_ngrok::JavaNetUrl<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let sess: MutexGuard = self.get_native(this); + let bldr = self.edge_builder(sess, jttb); + + let url = Url::parse(jurl.as_string(self.env).as_str()).expect("cannot parse url"); + + let tun = rt.block_on(bldr.listen_and_forward(url)); + match tun { + Ok(tun) => { + let jforwarder = ComNgrokNativeEdgeForwarder::new_1com_ngrok_native_edge_forwarder( + self.env, + tun.id().into(), + tun.metadata().into(), + tun.forwards_to().into(), + self.labels_map(tun.labels()), + ); + self.set_native(jforwarder, tun); + Ok(jforwarder) + } + Err(err) => io_exc_err(err), + } + } + + fn close_listener( + &self, + this: ComNgrokNativeSession<'local>, + id: String, + ) -> Result<(), jaffi_support::Error> { + self.close_tunnel(this, id) + } + + fn close_forwarder( + &self, + this: ComNgrokNativeSession<'local>, + id: String, + ) -> Result<(), jaffi_support::Error> { + self.close_tunnel(this, id) + } + + fn close( + &self, + this: ComNgrokNativeSession<'local>, + ) -> Result<(), jaffi_support::Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut sess: Session = self.take_native(this); + rt.block_on(sess.close()).map_err(io_exc) + } +} + +struct NativeTcpListenerRsImpl<'local> { + env: JNIEnv<'local>, +} + +impl<'local> JNIExt<'local> for NativeTcpListenerRsImpl<'local> { + fn get_env(&self) -> &JNIEnv<'local> { + &self.env + } +} + +impl<'local> com_ngrok::NativeTcpListenerRs<'local> for NativeTcpListenerRsImpl<'local> { + fn from_env(env: JNIEnv<'local>) -> Self { + Self { env } + } + + fn accept( + &self, + this: ComNgrokNativeTcpListener<'local>, + ) -> Result, Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut tun: MutexGuard = self.get_native(this); + match rt.block_on(tun.try_next()) { + Ok(Some(conn)) => { + let jconn: ComNgrokNativeEndpointConnection<'_> = + ComNgrokNativeEndpointConnection::new_1com_ngrok_native_endpoint_connection( + self.env, + conn.remote_addr().to_string(), + conn.proto().to_string(), + ); + self.set_native(jconn, conn); + Ok(jconn) + } + Ok(None) => io_exc_err("could not get next conn"), + Err(err) => io_exc_err(err), + } + } + + fn close(&self, this: ComNgrokNativeTcpListener<'local>) -> Result<(), Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut tun: TcpTunnel = self.take_native(this); rt.block_on(tun.close()).map_err(io_exc) } } @@ -936,24 +1119,24 @@ impl<'local> com_ngrok::NativeTcpForwarderRs<'local> for NativeTcpForwarderRsImp } } -struct NativeTlsTunnelRsImpl<'local> { +struct NativeTlsListenerRsImpl<'local> { env: JNIEnv<'local>, } -impl<'local> JNIExt<'local> for NativeTlsTunnelRsImpl<'local> { +impl<'local> JNIExt<'local> for NativeTlsListenerRsImpl<'local> { fn get_env(&self) -> &JNIEnv<'local> { &self.env } } -impl<'local> com_ngrok::NativeTlsTunnelRs<'local> for NativeTlsTunnelRsImpl<'local> { +impl<'local> com_ngrok::NativeTlsListenerRs<'local> for NativeTlsListenerRsImpl<'local> { fn from_env(env: JNIEnv<'local>) -> Self { Self { env } } fn accept( &self, - this: ComNgrokNativeTlsTunnel<'local>, + this: ComNgrokNativeTlsListener<'local>, ) -> Result, Error> { let rt = RT.get().expect("runtime not initialized"); @@ -974,19 +1157,41 @@ impl<'local> com_ngrok::NativeTlsTunnelRs<'local> for NativeTlsTunnelRsImpl<'loc } } - fn forward_tcp( - &self, - _this: ComNgrokNativeTlsTunnel<'local>, - _addr: String, - ) -> Result<(), Error> { - panic!("not implemented") - // let rt = RT.get().expect("runtime not initialized"); + fn close(&self, this: ComNgrokNativeTlsListener<'local>) -> Result<(), Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut tun: TlsTunnel = self.take_native(this); + rt.block_on(tun.close()).map_err(io_exc) + } +} - // let mut tun: MutexGuard = self.get_native(this); - // rt.block_on(tun.forward_tcp(addr)).map_err(io_exc) +struct NativeTlsForwarderRsImpl<'local> { + env: JNIEnv<'local>, +} + +impl<'local> JNIExt<'local> for NativeTlsForwarderRsImpl<'local> { + fn get_env(&self) -> &JNIEnv<'local> { + &self.env + } +} + +impl<'local> com_ngrok::NativeTlsForwarderRs<'local> for NativeTlsForwarderRsImpl<'local> { + fn from_env(env: JNIEnv<'local>) -> Self { + Self { env } + } + + fn join(&self, this: ComNgrokNativeTlsForwarder<'local>) -> Result<(), Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut tun: MutexGuard> = self.get_native(this); + match rt.block_on(tun.join()) { + Ok(Ok(())) => Ok(()), + Ok(Err(e)) => io_exc_err(e), + Err(e) => io_exc_err(e), + } } - fn close(&self, this: ComNgrokNativeTlsTunnel<'local>) -> Result<(), Error> { + fn close(&self, this: ComNgrokNativeTlsForwarder<'local>) -> Result<(), Error> { let rt = RT.get().expect("runtime not initialized"); let mut tun: TlsTunnel = self.take_native(this); @@ -994,24 +1199,24 @@ impl<'local> com_ngrok::NativeTlsTunnelRs<'local> for NativeTlsTunnelRsImpl<'loc } } -struct NativeHttpTunnelRsImpl<'local> { +struct NativeHttpListenerRsImpl<'local> { env: JNIEnv<'local>, } -impl<'local> JNIExt<'local> for NativeHttpTunnelRsImpl<'local> { +impl<'local> JNIExt<'local> for NativeHttpListenerRsImpl<'local> { fn get_env(&self) -> &JNIEnv<'local> { &self.env } } -impl<'local> com_ngrok::NativeHttpTunnelRs<'local> for NativeHttpTunnelRsImpl<'local> { +impl<'local> com_ngrok::NativeHttpListenerRs<'local> for NativeHttpListenerRsImpl<'local> { fn from_env(env: JNIEnv<'local>) -> Self { Self { env } } fn accept( &self, - this: ComNgrokNativeHttpTunnel<'local>, + this: ComNgrokNativeHttpListener<'local>, ) -> Result, Error> { let rt = RT.get().expect("runtime not initialized"); @@ -1032,21 +1237,46 @@ impl<'local> com_ngrok::NativeHttpTunnelRs<'local> for NativeHttpTunnelRsImpl<'l } } - fn forward_tcp( + fn close( &self, - _this: ComNgrokNativeHttpTunnel<'local>, - _addr: String, - ) -> Result<(), Error> { - panic!("not implemented") - // let rt = RT.get().expect("runtime not initialized"); + this: ComNgrokNativeHttpListener<'local>, + ) -> Result<(), jaffi_support::Error> { + let rt = RT.get().expect("runtime not initialized"); - // let mut tun: MutexGuard = self.get_native(this); - // rt.block_on(tun.forward_tcp(addr)).map_err(io_exc) + let mut tun: HttpTunnel = self.take_native(this); + rt.block_on(tun.close()).map_err(io_exc) + } +} + +struct NativeHttpForwarderRsImpl<'local> { + env: JNIEnv<'local>, +} + +impl<'local> JNIExt<'local> for NativeHttpForwarderRsImpl<'local> { + fn get_env(&self) -> &JNIEnv<'local> { + &self.env + } +} + +impl<'local> com_ngrok::NativeHttpForwarderRs<'local> for NativeHttpForwarderRsImpl<'local> { + fn from_env(env: JNIEnv<'local>) -> Self { + Self { env } + } + + fn join(&self, this: ComNgrokNativeHttpForwarder<'local>) -> Result<(), Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut tun: MutexGuard> = self.get_native(this); + match rt.block_on(tun.join()) { + Ok(Ok(())) => Ok(()), + Ok(Err(e)) => io_exc_err(e), + Err(e) => io_exc_err(e), + } } fn close( &self, - this: ComNgrokNativeHttpTunnel<'local>, + this: ComNgrokNativeHttpForwarder<'local>, ) -> Result<(), jaffi_support::Error> { let rt = RT.get().expect("runtime not initialized"); @@ -1055,24 +1285,24 @@ impl<'local> com_ngrok::NativeHttpTunnelRs<'local> for NativeHttpTunnelRsImpl<'l } } -struct NativeLabeledTunnelRsImpl<'local> { +struct NativeEdgeListenerRsImpl<'local> { env: JNIEnv<'local>, } -impl<'local> JNIExt<'local> for NativeLabeledTunnelRsImpl<'local> { +impl<'local> JNIExt<'local> for NativeEdgeListenerRsImpl<'local> { fn get_env(&self) -> &JNIEnv<'local> { &self.env } } -impl<'local> com_ngrok::NativeLabeledTunnelRs<'local> for NativeLabeledTunnelRsImpl<'local> { +impl<'local> com_ngrok::NativeEdgeListenerRs<'local> for NativeEdgeListenerRsImpl<'local> { fn from_env(env: JNIEnv<'local>) -> Self { Self { env } } fn accept( &self, - this: ComNgrokNativeLabeledTunnel<'local>, + this: ComNgrokNativeEdgeListener<'local>, ) -> Result, Error> { let rt = RT.get().expect("runtime not initialized"); @@ -1099,21 +1329,46 @@ impl<'local> com_ngrok::NativeLabeledTunnelRs<'local> for NativeLabeledTunnelRsI } } - fn forward_tcp( + fn close( &self, - _this: ComNgrokNativeLabeledTunnel<'local>, - _addr: String, - ) -> Result<(), Error> { - panic!("not implemented") - // let rt = RT.get().expect("runtime not initialized"); + this: ComNgrokNativeEdgeListener<'local>, + ) -> Result<(), jaffi_support::Error> { + let rt = RT.get().expect("runtime not initialized"); - // let mut tun: MutexGuard = self.get_native(this); - // rt.block_on(tun.forward_tcp(addr)).map_err(io_exc) + let mut tun: LabeledTunnel = self.take_native(this); + rt.block_on(tun.close()).map_err(io_exc) + } +} + +struct NativeEdgeForwarderRsImpl<'local> { + env: JNIEnv<'local>, +} + +impl<'local> JNIExt<'local> for NativeEdgeForwarderRsImpl<'local> { + fn get_env(&self) -> &JNIEnv<'local> { + &self.env + } +} + +impl<'local> com_ngrok::NativeEdgeForwarderRs<'local> for NativeEdgeForwarderRsImpl<'local> { + fn from_env(env: JNIEnv<'local>) -> Self { + Self { env } + } + + fn join(&self, this: ComNgrokNativeEdgeForwarder<'local>) -> Result<(), Error> { + let rt = RT.get().expect("runtime not initialized"); + + let mut tun: MutexGuard> = self.get_native(this); + match rt.block_on(tun.join()) { + Ok(Ok(())) => Ok(()), + Ok(Err(e)) => io_exc_err(e), + Err(e) => io_exc_err(e), + } } fn close( &self, - this: ComNgrokNativeLabeledTunnel<'local>, + this: ComNgrokNativeEdgeForwarder<'local>, ) -> Result<(), jaffi_support::Error> { let rt = RT.get().expect("runtime not initialized"); diff --git a/ngrok-java/src/main/java/com/ngrok/TunnelInfo.java b/ngrok-java-native/src/main/java/com/ngrok/AbstractEdge.java similarity index 57% rename from ngrok-java/src/main/java/com/ngrok/TunnelInfo.java rename to ngrok-java-native/src/main/java/com/ngrok/AbstractEdge.java index c806ce8..ee11d9d 100644 --- a/ngrok-java/src/main/java/com/ngrok/TunnelInfo.java +++ b/ngrok-java-native/src/main/java/com/ngrok/AbstractEdge.java @@ -1,25 +1,33 @@ package com.ngrok; -public abstract class TunnelInfo { +import java.util.Map; + +public class AbstractEdge { private final String id; - private final String forwardsTo; private final String metadata; + private final String forwardsTo; + private final Map labels; - public TunnelInfo(String id, String forwardsTo, String metadata) { + public AbstractEdge(String id, String metadata, String forwardsTo, Map labels) { this.id = id; - this.forwardsTo = forwardsTo; this.metadata = metadata; + this.forwardsTo = forwardsTo; + this.labels = labels; } public String getId() { return id; } + public String getMetadata() { + return metadata; + } + public String getForwardsTo() { return forwardsTo; } - public String getMetadata() { - return metadata; + public Map getLabels() { + return labels; } } diff --git a/ngrok-java-native/src/main/java/com/ngrok/AbstractEndpoint.java b/ngrok-java-native/src/main/java/com/ngrok/AbstractEndpoint.java new file mode 100644 index 0000000..92cd78c --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/AbstractEndpoint.java @@ -0,0 +1,37 @@ +package com.ngrok; + +public abstract class AbstractEndpoint { + private final String id; + private final String metadata; + private final String forwardsTo; + private final String proto; + private final String url; + + public AbstractEndpoint(String id, String metadata, String forwardsTo, String proto, String url) { + this.id = id; + this.metadata = metadata; + this.forwardsTo = forwardsTo; + this.proto = proto; + this.url = url; + } + + public String getId() { + return id; + } + + public String getMetadata() { + return metadata; + } + + public String getForwardsTo() { + return forwardsTo; + } + + public String getProto() { + return proto; + } + + public String getUrl() { + return url; + } +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeConnection.java b/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeConnection.java index 4711afc..9508b55 100644 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeConnection.java +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeConnection.java @@ -6,16 +6,37 @@ /** * An implementation of {@link Connection} that delegates calls to a native library. */ -public class NativeEdgeConnection extends EdgeConnection { +public class NativeEdgeConnection implements Connection.Edge { private long native_address; + private final String remoteAddr; + private final String edgeType; + private final boolean passthroughTls; + /** * Constructs a new connection with the specified remote address. * * @param remoteAddr the remote address of the connection */ - public NativeEdgeConnection(String remoteAddr, String type, boolean passthroughTls) { - super(remoteAddr, type, passthroughTls); + public NativeEdgeConnection(String remoteAddr, String edgeType, boolean passthroughTls) { + this.remoteAddr = remoteAddr; + this.edgeType = edgeType; + this.passthroughTls = passthroughTls; + } + + @Override + public String getRemoteAddr() { + return remoteAddr; + } + + @Override + public String getEdgeType() { + return edgeType; + } + + @Override + public boolean isPassthroughTls() { + return passthroughTls; } /** diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeForwarder.java b/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeForwarder.java new file mode 100644 index 0000000..8db98fc --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeForwarder.java @@ -0,0 +1,18 @@ +package com.ngrok; + +import java.io.IOException; +import java.util.Map; + +public class NativeEdgeForwarder extends AbstractEdge implements Forwarder.Edge { + private long native_address; + + public NativeEdgeForwarder(String id, String metadata, String forwardsTo, Map labels) { + super(id, metadata, forwardsTo, labels); + } + + @Override + public native void join() throws IOException; + + @Override + public native void close() throws IOException; +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeListener.java b/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeListener.java new file mode 100644 index 0000000..d19381f --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeEdgeListener.java @@ -0,0 +1,18 @@ +package com.ngrok; + +import java.io.IOException; +import java.util.Map; + +public class NativeEdgeListener extends AbstractEdge implements Listener.Edge { + private long native_address; + + public NativeEdgeListener(String id, String metadata, String forwardsTo, Map labels) { + super(id, metadata, forwardsTo, labels); + } + + @Override + public native NativeEdgeConnection accept() throws IOException; + + @Override + public native void close() throws IOException; +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeEndpointConnection.java b/ngrok-java-native/src/main/java/com/ngrok/NativeEndpointConnection.java index 74d212e..341e271 100644 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeEndpointConnection.java +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeEndpointConnection.java @@ -6,16 +6,30 @@ /** * An implementation of {@link Connection} that delegates calls to a native library. */ -public class NativeEndpointConnection extends EndpointConnection { +public class NativeEndpointConnection implements Connection.Endpoint { private long native_address; + private final String proto; + + private final String remoteAddr; + /** * Constructs a new connection with the specified remote address. * * @param remoteAddr the remote address of the connection */ public NativeEndpointConnection(String remoteAddr, String proto) { - super(remoteAddr, proto); + this.proto = proto; + this.remoteAddr = remoteAddr; + } + + public String getProto() { + return proto; + } + + @Override + public String getRemoteAddr() { + return remoteAddr; } /** diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeHttpForwarder.java b/ngrok-java-native/src/main/java/com/ngrok/NativeHttpForwarder.java new file mode 100644 index 0000000..8871c80 --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeHttpForwarder.java @@ -0,0 +1,17 @@ +package com.ngrok; + +import java.io.IOException; + +public class NativeHttpForwarder extends AbstractEndpoint implements Forwarder.Endpoint { + private long native_address; + + public NativeHttpForwarder(String id, String metadata, String forwardsTo, String proto, String url) { + super(id, metadata, forwardsTo, proto, url); + } + + @Override + public native void join() throws IOException; + + @Override + public native void close() throws IOException; +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeHttpListener.java b/ngrok-java-native/src/main/java/com/ngrok/NativeHttpListener.java new file mode 100644 index 0000000..161d37d --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeHttpListener.java @@ -0,0 +1,17 @@ +package com.ngrok; + +import java.io.IOException; + +public class NativeHttpListener extends AbstractEndpoint implements Listener.Endpoint { + private long native_address; + + public NativeHttpListener(String id, String metadata, String forwardsTo, String proto, String url) { + super(id, metadata, forwardsTo, proto, url); + } + + @Override + public native NativeEndpointConnection accept() throws IOException; + + @Override + public native void close() throws IOException; +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeHttpTunnel.java b/ngrok-java-native/src/main/java/com/ngrok/NativeHttpTunnel.java deleted file mode 100644 index a4917fe..0000000 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeHttpTunnel.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ngrok; - -import java.io.IOException; - -/** -* An implementation of {@link HttpTunnel} that delegates calls to a native library. -*/ -public class NativeHttpTunnel extends HttpTunnel { - private long native_address; - - /** - * Constructs a new HTTP tunnel with the specified ID, forwarding address, - * metadata, protocol, and URL. - * - * @param id the ID of the tunnel - * @param forwardsTo the forwarding address of the tunnel - * @param metadata the metadata of the tunnel - * @param proto the protocol of the tunnel - * @param url the URL of the tunnel - */ - public NativeHttpTunnel(String id, String forwardsTo, String metadata, String proto, String url) { - super(id, forwardsTo, metadata, proto, url); - } - - /** - * Accepts a new connection on the HTTP tunnel. Blocks until one is available. - * - * @return a new NativeConnection object representing the accepted connection - * @throws IOException if an I/O error occurs - */ - @Override - public native NativeEndpointConnection accept() throws IOException; - - /** - * Forwards TCP traffic to the specified address. Blocking until closed. - * - * @param addr the address to forward TCP traffic to. Example: 127.0.0.1 - * @throws IOException if an I/O error occurs - */ - @Override - public native void forwardTcp(String addr) throws IOException; - - /** - * Closes the HTTP tunnel. - * - * @throws IOException if an I/O error occurs - */ - @Override - public native void close() throws IOException; -} \ No newline at end of file diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeLabeledTunnel.java b/ngrok-java-native/src/main/java/com/ngrok/NativeLabeledTunnel.java deleted file mode 100644 index a4f90b7..0000000 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeLabeledTunnel.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.ngrok; - -import java.io.IOException; - -/** -* An implementation of {@link LabeledTunnel} that delegates calls to a native library. - */ -public class NativeLabeledTunnel extends LabeledTunnel { - private long native_address; - - /** - * Constructs a new {@link NativeLabeledTunnel} with the specified ID, forwarding address, and metadata. - * - * @param id the ID of the tunnel - * @param forwardsTo the forwarding address of the tunnel - * @param metadata the metadata of the tunnel - */ - public NativeLabeledTunnel(String id, String forwardsTo, String metadata) { - super(id, forwardsTo, metadata); - } - - /** - * Accepts a new connection on the labeled tunnel. - * - * @return a new NativeConnection object representing the accepted connection - * @throws IOException if an I/O error occurs - */ - @Override - public native NativeEdgeConnection accept() throws IOException; - - /** - * Forwards TCP traffic to the specified address. - * - * @param addr the address to forward TCP traffic to - * @throws IOException if an I/O error occurs - */ - @Override - public native void forwardTcp(String addr) throws IOException; - - /** - * Closes the labeled tunnel. - * - * @throws IOException if an I/O error occurs - */ - @Override - public native void close() throws IOException; -} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeSession.java b/ngrok-java-native/src/main/java/com/ngrok/NativeSession.java index 09765fa..0e0f44e 100644 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeSession.java +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeSession.java @@ -1,6 +1,7 @@ package com.ngrok; import java.io.IOException; +import java.net.URL; import java.util.Properties; /** @@ -74,46 +75,34 @@ public String getMetadata() { } @Override - public native NativeTcpListener listenTcp(Tcp.Builder builder) throws IOException; + public native NativeTcpListener listenTcp(TcpBuilder builder) throws IOException; @Override - public native NativeTcpForwarder forwardTcp(Tcp.Builder builder, String url) throws IOException; + public native NativeTcpForwarder forwardTcp(TcpBuilder builder, URL url) throws IOException; - /** - * Creates a new {@link NativeTlsTunnel} using the specified builder. - * - * @param builder the builder to use for the tunnel - * @return a new NativeTlsTunnel object representing the created tunnel - * @throws IOException if an I/O error occurs - */ - public native NativeTlsTunnel tlsTunnel(NativeTlsTunnel.Builder builder) throws IOException; + @Override + public native NativeTlsListener listenTls(TlsBuilder builder) throws IOException; - /** - * Creates a new {@link NativeHttpTunnel} using the specified builder. - * - * @param builder the builder to use for the tunnel - * @return a new NativeHttpTunnel object representing the created tunnel - * @throws IOException if an I/O error occurs - */ - public native NativeHttpTunnel httpTunnel(NativeHttpTunnel.Builder builder) throws IOException; + @Override + public native NativeTlsForwarder forwardTls(TlsBuilder builder, URL url) throws IOException; - /** - * Creates a new {@link NativeLabeledTunnel} using the specified builder. - * - * @param builder the builder to use for the tunnel - * @return a new NativeLabeledTunnel object representing the created tunnel - * @throws IOException if an I/O error occurs - */ - public native NativeLabeledTunnel labeledTunnel(NativeLabeledTunnel.Builder builder) throws IOException; + @Override + public native NativeHttpListener listenHttp(HttpBuilder builder) throws IOException; + + @Override + public native NativeHttpForwarder forwardHttp(HttpBuilder builder, URL url) throws IOException; + + @Override + public native NativeEdgeListener listenEdge(EdgeBuilder builder) throws IOException; + + @Override + public native NativeEdgeForwarder forwardEdge(EdgeBuilder builder, URL url) throws IOException; + + @Override + public native void closeListener(String id) throws IOException; - /** - * Closes the native tunnel by ID in this session. - * - * @param tunnelId the ID of the tunnel to close - * @throws IOException if an I/O error occurs - */ @Override - public native void closeTunnel(String tunnelId) throws IOException; + public native void closeForwarder(String id) throws IOException; /** * Closes the native session. diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeTcpForwarder.java b/ngrok-java-native/src/main/java/com/ngrok/NativeTcpForwarder.java index dc48df6..402abb4 100644 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeTcpForwarder.java +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeTcpForwarder.java @@ -2,18 +2,11 @@ import java.io.IOException; -public class NativeTcpForwarder implements Tcp.Forwarder { +public class NativeTcpForwarder extends AbstractEndpoint implements Forwarder.Endpoint { private long native_address; - private final EndpointInfo info; - - public NativeTcpForwarder(EndpointInfo info) { - this.info = info; - } - - @Override - public EndpointInfo info() { - return info; + public NativeTcpForwarder(String id, String metadata, String forwardsTo, String proto, String url) { + super(id, metadata, forwardsTo, proto, url); } @Override diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeTcpListener.java b/ngrok-java-native/src/main/java/com/ngrok/NativeTcpListener.java index 82eba43..759cb7f 100644 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeTcpListener.java +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeTcpListener.java @@ -2,18 +2,11 @@ import java.io.IOException; -public class NativeTcpListener implements Tcp.Listener { +public class NativeTcpListener extends AbstractEndpoint implements Listener.Endpoint { private long native_address; - private final EndpointInfo info; - - public NativeTcpListener(EndpointInfo info) { - this.info = info; - } - - @Override - public EndpointInfo info() { - return info; + public NativeTcpListener(String id, String metadata, String forwardsTo, String proto, String url) { + super(id, metadata, forwardsTo, proto, url); } @Override diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeTlsForwarder.java b/ngrok-java-native/src/main/java/com/ngrok/NativeTlsForwarder.java new file mode 100644 index 0000000..79e5a49 --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeTlsForwarder.java @@ -0,0 +1,17 @@ +package com.ngrok; + +import java.io.IOException; + +public class NativeTlsForwarder extends AbstractEndpoint implements Forwarder.Endpoint { + private long native_address; + + public NativeTlsForwarder(String id, String metadata, String forwardsTo, String proto, String url) { + super(id, metadata, forwardsTo, proto, url); + } + + @Override + public native void join() throws IOException; + + @Override + public native void close() throws IOException; +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeTlsListener.java b/ngrok-java-native/src/main/java/com/ngrok/NativeTlsListener.java new file mode 100644 index 0000000..0517ffd --- /dev/null +++ b/ngrok-java-native/src/main/java/com/ngrok/NativeTlsListener.java @@ -0,0 +1,17 @@ +package com.ngrok; + +import java.io.IOException; + +public class NativeTlsListener extends AbstractEndpoint implements Listener.Endpoint { + private long native_address; + + public NativeTlsListener(String id, String metadata, String forwardsTo, String proto, String url) { + super(id, metadata, forwardsTo, proto, url); + } + + @Override + public native NativeEndpointConnection accept() throws IOException; + + @Override + public native void close() throws IOException; +} diff --git a/ngrok-java-native/src/main/java/com/ngrok/NativeTlsTunnel.java b/ngrok-java-native/src/main/java/com/ngrok/NativeTlsTunnel.java deleted file mode 100644 index ef5919d..0000000 --- a/ngrok-java-native/src/main/java/com/ngrok/NativeTlsTunnel.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.ngrok; - -import java.io.IOException; - -/** -* An implementation of {@link TlsTunnel} that delegates calls to a native library. - */ -public class NativeTlsTunnel extends TlsTunnel { - private long native_address; - - /** - * Constructs a new TLS tunnel with the specified ID, forwarding address, - * metadata, protocol, and URL. - * - * @param id the ID of the tunnel - * @param forwardsTo the forwarding address of the tunnel - * @param metadata the metadata of the tunnel - * @param proto the protocol of the tunnel - * @param url the URL of the tunnel - */ - public NativeTlsTunnel(String id, String forwardsTo, String metadata, String proto, String url) { - super(id, forwardsTo, metadata, proto, url); - } - - /** - * Accepts a new connection on the TLS tunnel. - * - * @return a new NativeConnection object representing the accepted connection - * @throws IOException if an I/O error occurs - */ - @Override - public native NativeEndpointConnection accept() throws IOException; - - /** - * Forwards TCP traffic to the specified address. - * - * @param addr the address to forward TCP traffic to - * @throws IOException if an I/O error occurs - */ - @Override - public native void forwardTcp(String addr) throws IOException; - - /** - * Closes the TLS tunnel. - * - * @throws IOException if an I/O error occurs - */ - @Override - public native void close() throws IOException; -} diff --git a/ngrok-java-native/src/test/java/com/ngrok/DataTest.java b/ngrok-java-native/src/test/java/com/ngrok/DataTest.java index 09a92a7..eb0368a 100644 --- a/ngrok-java-native/src/test/java/com/ngrok/DataTest.java +++ b/ngrok-java-native/src/test/java/com/ngrok/DataTest.java @@ -12,31 +12,30 @@ public class DataTest { @Before public void setup() { System.setProperty("org.slf4j.simpleLogger.log.com.ngrok.Runtime", "trace"); - } @Test public void testSessionClose() throws Exception { - try (var session = Session.connect(Session.newBuilder().metadata("java-session"))) { + try (var session = Session.withAuthtokenFromEnv().metadata("java-session").connect()) { assertEquals("java-session", session.getMetadata()); } } @Test public void testTunnelClose() throws Exception { - try (var session = Session.connect(Session.newBuilder()); - var tunnel = session.httpTunnel(new HttpTunnel.Builder().metadata("java-tunnel"))) { - assertEquals("java-tunnel", tunnel.getMetadata()); - Runtime.getLogger().log("info", "session", tunnel.getUrl()); + try (var session = Session.withAuthtokenFromEnv().connect(); + var listener = session.httpEndpoint().metadata("java-tunnel").listen()) { + assertEquals("java-tunnel", listener.getMetadata()); + Runtime.getLogger().log("info", "session", listener.getUrl()); } } // @Test public void testPingPong() throws Exception { - var session = Session.connect(Session.newBuilder()); + var session = Session.withAuthtokenFromEnv().connect(); assertNotNull(session); - var tunnel = session.listenTcp(); + var tunnel = session.tcpEndpoint().listen(); assertNotNull(tunnel); var conn = tunnel.accept(); diff --git a/ngrok-java-native/src/test/java/com/ngrok/ForwardTest.java b/ngrok-java-native/src/test/java/com/ngrok/ForwardTest.java index ff10710..dfa36ff 100644 --- a/ngrok-java-native/src/test/java/com/ngrok/ForwardTest.java +++ b/ngrok-java-native/src/test/java/com/ngrok/ForwardTest.java @@ -2,28 +2,24 @@ import org.junit.Test; +import java.net.URL; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class ForwardTest { - // @Test +// @Test public void testForward() throws Exception { - var session = Session.connect(Session.newBuilder()); + var session = Session.withAuthtokenFromEnv().connect(); assertNotNull(session); - var tunnel = session.httpTunnel(new HttpTunnel.Builder().domain("ngrok-java-test.ngrok.io")); - assertNotNull(tunnel); + var listener = session.httpEndpoint().domain("ngrok-java-test.ngrok.io") + .forward(new URL("127.0.0.1:8000")); + assertNotNull(listener); - new Thread(() -> { - try { - Thread.sleep(10000); - session.closeTunnel(tunnel.getId()); - } catch(Throwable th) { - th.printStackTrace(); - } - }).start(); + Thread.sleep(10000); + session.closeListener(listener.getId()); - tunnel.forwardTcp("127.0.0.1:8000"); assertTrue(true); } } \ No newline at end of file diff --git a/ngrok-java/src/main/java/com/ngrok/Connection.java b/ngrok-java/src/main/java/com/ngrok/Connection.java index 6a1f799..f77563a 100644 --- a/ngrok-java/src/main/java/com/ngrok/Connection.java +++ b/ngrok-java/src/main/java/com/ngrok/Connection.java @@ -7,34 +7,11 @@ /** * Represents a connection established over a tunnel. */ -public abstract class Connection implements AutoCloseable { - private final String remoteAddr; +public interface Connection extends AutoCloseable { + String getRemoteAddr(); - /** - * Creates a new connection with the given remote address. - * - * @param remoteAddr the remote address to connect to - */ - public Connection(String remoteAddr) { - this.remoteAddr = remoteAddr; - } - - /** - * Retrieves the remote address of the connection. - * - * @return the remote address of the connection - */ - public String getRemoteAddr() { - return remoteAddr; - } - - /** - * Creates an {@link InetSocketAddress} for this connection's remote address. - * - * @return {@link InetSocketAddress} representing the internet address - */ - public InetSocketAddress inetAddress() { - var parts = remoteAddr.split(":"); + default InetSocketAddress inetAddress() { + var parts = getRemoteAddr().split(":"); return new InetSocketAddress(parts[0], Integer.parseInt(parts[1])); } @@ -45,7 +22,7 @@ public InetSocketAddress inetAddress() { * @return the number of bytes read, or -1 if the end of the stream has been reached * @throws IOException if an I/O error occurs */ - public abstract int read(ByteBuffer dst) throws IOException; + int read(ByteBuffer dst) throws IOException; /** * Writes a sequence of bytes to this connection from the given buffer. @@ -54,12 +31,22 @@ public InetSocketAddress inetAddress() { * @return the number of bytes written * @throws IOException if an I/O error occurs */ - public abstract int write(ByteBuffer src) throws IOException; + int write(ByteBuffer src) throws IOException; /** * Closes this connection and releases any system resources associated with it. * * @throws IOException if an I/O error occurs */ - public abstract void close() throws IOException; + void close() throws IOException; + + interface Endpoint extends Connection { + String getProto(); + } + + interface Edge extends Connection { + String getEdgeType(); + + boolean isPassthroughTls(); + } } \ No newline at end of file diff --git a/ngrok-java/src/main/java/com/ngrok/EdgeBuilder.java b/ngrok-java/src/main/java/com/ngrok/EdgeBuilder.java new file mode 100644 index 0000000..55e3213 --- /dev/null +++ b/ngrok-java/src/main/java/com/ngrok/EdgeBuilder.java @@ -0,0 +1,91 @@ +package com.ngrok; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * A builder for a {@link EdgeBuilder}. + */ +public class EdgeBuilder extends MetadataBuilder + implements Listener.Builder, Forwarder.Builder { + private final Session session; + + private final Map labels = new HashMap<>(); + + public EdgeBuilder(Session session) { + this.session = session; + } + + /** + * Adds a label with the specified key and value to this builder. + * + * @param key the key of the label + * @param value the value of the label + * @return the builder instance + */ + public EdgeBuilder label(String key, String value) { + labels.put(Objects.requireNonNull(key), Objects.requireNonNull(value)); + return this; + } + + /** + * Generates and returns a list of unique labels for this builder. + * + * @return a list of unique labels for this builder + */ + public List