diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..3003493
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+end_of_line = lf
+insert_final_newline = true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4cb41e9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,188 @@
+### Vert.x ###
+.vertx/
+
+### Eclipse ###
+
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+### Intellij+iml ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+
+# Sensitive or high-churn files:
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.xml
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+
+# Gradle:
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# CMake
+cmake-buildTool-debug/
+
+# Mongo Explorer plugin:
+.idea/**/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-buildTool.properties
+fabric.properties
+
+### Intellij+iml Patch ###
+# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
+
+*.iml
+modules.xml
+.idea/misc.xml
+*.ipr
+
+### macOS ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Maven ###
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+
+# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
+!/.mvn/wrapper/maven-wrapper.jar
+
+### Gradle ###
+.gradle
+/buildTool/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+### NetBeans ###
+nbproject/private/
+buildTool/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+.classpath
+.project
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..4c4af45
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,32 @@
+= Gemini
+
+image:https://img.shields.io/badge/vert.x-4.4.1-purple.svg[link="https://vertx.io"]
+
+This application was generated using http://start.vertx.io
+
+== Building
+
+To launch your tests:
+```
+./mvnw clean test
+```
+
+To package your application:
+```
+./mvnw clean package
+```
+
+To run your application:
+```
+./mvnw clean compile exec:java
+```
+
+== Help
+
+* https://vertx.io/docs/[Vert.x Documentation]
+* https://stackoverflow.com/questions/tagged/vert.x?sort=newest&pageSize=15[Vert.x Stack Overflow]
+* https://groups.google.com/forum/?fromgroups#!forum/vertx[Vert.x User Group]
+* https://discord.gg/6ry7aqPWXy[Vert.x Discord]
+* https://gitter.im/eclipse-vertx/vertx-users[Vert.x Gitter]
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d7bce5d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,137 @@
+
+
+ 4.0.0
+
+ cx.lehmann.gemini
+ gemini-proxy
+ 1.0.0-SNAPSHOT
+
+
+ UTF-8
+
+ 3.8.1
+ 3.2.4
+ 2.22.2
+ 3.0.0
+
+ 4.4.4
+ 5.9.1
+
+ cx.lehmann.gemini.gemini_proxy.MainVerticle
+ io.vertx.core.Launcher
+
+
+
+
+
+ io.vertx
+ vertx-stack-depchain
+ ${vertx.version}
+ pom
+ import
+
+
+
+
+
+
+ io.vertx
+ vertx-core
+
+
+
+ io.vertx
+ vertx-junit5
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit-jupiter.version}
+ test
+
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.7
+
+
+
+ org.slf4j
+ slf4j-simple
+ 2.0.7
+ runtime
+
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.4.0-b180830.0359
+
+
+
+
+
+
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ 11
+
+
+
+ maven-shade-plugin
+ ${maven-shade-plugin.version}
+
+
+ package
+
+ shade
+
+
+
+
+
+ ${launcher.class}
+ ${main.verticle}
+
+
+
+
+ ${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
+
+
+
+
+
+
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+ io.vertx.core.Launcher
+
+ run
+ ${main.verticle}
+
+
+
+
+
+
+
+
diff --git a/src/main/java/cx/lehmann/gemini/gemini_proxy/MainVerticle.java b/src/main/java/cx/lehmann/gemini/gemini_proxy/MainVerticle.java
new file mode 100644
index 0000000..bd0b671
--- /dev/null
+++ b/src/main/java/cx/lehmann/gemini/gemini_proxy/MainVerticle.java
@@ -0,0 +1,102 @@
+package cx.lehmann.gemini.gemini_proxy;
+
+import java.util.Date;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.Promise;
+import io.vertx.core.net.NetClient;
+import io.vertx.core.net.NetClientOptions;
+import io.vertx.core.net.NetServerOptions;
+import io.vertx.core.net.NetSocket;
+import io.vertx.core.net.PemKeyCertOptions;
+import io.vertx.core.net.ProxyOptions;
+import io.vertx.core.net.ProxyType;
+import io.vertx.core.streams.Pump;
+
+public class MainVerticle extends AbstractVerticle {
+
+ private final static Logger log = LoggerFactory.getLogger(MainVerticle.class);
+
+ @Override
+ public void start(Promise startPromise) throws Exception {
+ NetServerOptions serverOptions=new NetServerOptions();
+ NetClientOptions clientOptions=new NetClientOptions();
+
+ ProxyOptions proxyOptions=new ProxyOptions();
+ proxyOptions.setHost("localhost")
+ .setPort(9150)
+ .setType(ProxyType.SOCKS5);
+
+ clientOptions.setProxyOptions(proxyOptions)
+ .setTrustAll(true)
+ .setSsl(true);
+
+ NetClient client=vertx.createNetClient(clientOptions);
+
+ vertx.exceptionHandler(ex -> {ex.printStackTrace();});
+
+ String certPath="c:/temp/cert.pem";
+// String certPath="/home/lehmann/gemini-chat/cert.pem";
+
+ serverOptions.setPemKeyCertOptions(new PemKeyCertOptions()
+ .setCertPath(certPath)
+ .setKeyPath(certPath))
+ .setSsl(true);
+
+ vertx.createNetServer(serverOptions).connectHandler(conn -> {
+ conn.handler(event -> {
+ System.out.println(new Date().toString()+" accepted connection:"+conn.remoteAddress());
+ String line=event.toString("UTF-8");
+ if(!line.endsWith("\r\n")) {
+ conn.write("40 format error\r\n");
+ conn.close();
+ } else {
+ String url=line.substring(0, line.length()-2);
+
+ if(!url.startsWith("gemini://")) {
+ conn.write("40 format error\r\n");
+ conn.close();
+ }
+ else {
+ String url1=url.substring(9);
+ log.info("url:{}",url1);
+ int firstSlash=url1.indexOf('/');
+ String hostname=url1.substring(0,firstSlash);
+ String path=url1.substring(firstSlash);
+ client.connect(1965, hostname, res -> {
+ if(res.succeeded()) {
+ NetSocket clientConn = res.result();
+ clientConn.write(url+"\r\n");
+ Pump pump=Pump.pump(clientConn, conn).start();
+ clientConn.closeHandler(v -> {
+ pump.stop();
+ conn.close();
+ });
+ conn.closeHandler(v -> {
+ pump.stop();
+ clientConn.close();
+ });
+ } else {
+ log.info("connection failed", res.cause());
+ conn.write("40 connection error"+res.cause().getMessage()+"\r\n");
+ conn.close();
+ }
+ });
+ }
+ }
+ }
+ );
+ }).listen(1965, server -> {
+ if (server.succeeded()) {
+ startPromise.complete();
+ System.out.println("Gemini proxy started on port 1965 at "+new Date().toString());
+ } else {
+ server.cause().printStackTrace();
+ startPromise.fail(server.cause());
+ }
+ });
+ }
+}
diff --git a/src/test/java/cx/lehmann/gemini/gemini_proxy/TestMainVerticle.java b/src/test/java/cx/lehmann/gemini/gemini_proxy/TestMainVerticle.java
new file mode 100644
index 0000000..55b8a31
--- /dev/null
+++ b/src/test/java/cx/lehmann/gemini/gemini_proxy/TestMainVerticle.java
@@ -0,0 +1,23 @@
+package cx.lehmann.gemini.gemini_proxy;
+
+//import io.vertx.core.Vertx;
+//import io.vertx.junit5.VertxExtension;
+//import io.vertx.junit5.VertxTestContext;
+//import org.junit.jupiter.api.BeforeEach;
+//import org.junit.jupiter.api.Test;
+//import org.junit.jupiter.api.extension.ExtendWith;
+
+//@ExtendWith(VertxExtension.class)
+public class TestMainVerticle {
+
+// @BeforeEach
+// void deploy_verticle(Vertx vertx, VertxTestContext testContext) {
+// vertx.deployVerticle(new MainVerticle(), testContext.succeeding(id -> testContext.completeNow()));
+// }
+//
+// @Test
+// void verticle_deployed(Vertx vertx, VertxTestContext testContext) throws Throwable {
+// vertx.setTimer(10000000, l ->
+// testContext.completeNow());
+// }
+}