commit 99b2ef4a07275a0dd3e6fcc15bfc681d3be53b8a from: Omar Polo date: Wed Oct 13 08:00:08 2021 UTC rework the Gemini API commit - d8c723115cf04e9d7d30fb3de5f4350fc895786a commit + 99b2ef4a07275a0dd3e6fcc15bfc681d3be53b8a blob - 1ecc5d408ab20f8d8457d1ebdc28afe9dd08a032 (mode 644) blob + /dev/null --- java/com/omarpolo/gemini/Gemini.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.omarpolo.gemini; - -import javax.net.ssl.*; -import java.io.*; -import java.net.Socket; -import java.net.URL; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.Collections; -import java.util.NoSuchElementException; -import java.util.Scanner; - -public class Gemini { - public static class DummyManager extends X509ExtendedTrustManager { - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) { - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) { - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - } - - public static class MalformedResponse extends Exception { - public MalformedResponse() {} - public MalformedResponse(String msg) { - super(msg); - } - } - - public static class Response implements AutoCloseable { - private final BufferedReader in; - private final PrintWriter out; - private final SSLSocket sock; - - private final int code; - private final String meta; - - private Response(PrintWriter out, SSLSocket sock) throws IOException, MalformedResponse { - var inStream = sock.getInputStream(); - this.in = new BufferedReader(new InputStreamReader(inStream)); - this.out = out; - this.sock = sock; - - var reply = in.readLine(); - if (reply.length() > 1027) { - throw new MalformedResponse("reply header too long"); - } - - var s = new Scanner(new StringReader(reply)); - try { - code = s.nextInt(); - s.skip(" "); - meta = s.nextLine(); - } catch (NoSuchElementException e) { - throw new MalformedResponse(); - } - } - - public int getCode() { - return code; - } - - public String getMeta() { - return meta; - } - - public BufferedReader body() { - return in; - } - - public void close() throws IOException { - in.close(); - out.close(); - sock.close(); - } - } - - public static SSLSocket connect(String host, int port) throws IOException { - try { - var params = new SSLParameters(); - params.setServerNames(Collections.singletonList(new SNIHostName(host))); - - var ctx = SSLContext.getInstance("TLS"); - ctx.init(null, new DummyManager[]{new DummyManager()}, new SecureRandom()); - var factory = (SSLSocketFactory) ctx.getSocketFactory(); - - var socket = (SSLSocket) factory.createSocket(host, port); - socket.setSSLParameters(params); - socket.startHandshake(); - return socket; - } - catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new RuntimeException("Unexpected failure", e); - } - } - - public static Response get(URL url) throws IOException, MalformedResponse { - int port = url.getPort(); - if (port == -1) { - port = 1965; - } - - String req = url.toString() + "\r\n"; - return get(url.getHost(), port, req); - } - - public static Response get(String host, int port, String req) throws IOException, MalformedResponse { - var sock = connect(host, port); - - var outStream = sock.getOutputStream(); - var out = new PrintWriter( - new BufferedWriter(new OutputStreamWriter(outStream))); - - out.print(req); - out.flush(); - - return new Response(out, sock); - } -} blob - /dev/null blob + 927d6ec28e9215b8404f4807c36e635eb3d570ea (mode 644) --- /dev/null +++ java/com/omarpolo/gemini/Response.java @@ -0,0 +1,131 @@ +package com.omarpolo.gemini; + +import javax.net.ssl.*; +import java.io.*; +import java.net.Socket; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.NoSuchElementException; +import java.util.Scanner; + +public class Response implements AutoCloseable { + + private final BufferedReader in; + private final PrintWriter out; + private final SSLSocket sock; + + private final int code; + private final String meta; + + public static class DummyManager extends X509ExtendedTrustManager { + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } + + public static class MalformedResponse extends Exception {} + + public Response(URL url) throws IOException, MalformedResponse { + this(url.getHost(), url.getPort(), url.toString() + "\r\n"); + } + + public Response(String host, int port, String req) throws IOException, MalformedResponse { + if (port == -1) { + port = 1965; + } + + sock = connect(host, port); + + var outStream = sock.getOutputStream(); + out = new PrintWriter( + new BufferedWriter(new OutputStreamWriter(outStream))); + + out.print(req); + out.flush(); + + var inStream = sock.getInputStream(); + in = new BufferedReader(new InputStreamReader(inStream)); + + var reply = in.readLine(); + + if (reply.length() > 1027) { + throw new MalformedResponse(); + } + + var s = new Scanner(new StringReader(reply)); + try { + code = s.nextInt(); + s.skip(" "); + meta = s.nextLine(); + } catch (NoSuchElementException e) { + throw new MalformedResponse(); + } + } + + public SSLSocket connect(String host, int port) throws IOException { + try { + var params = new SSLParameters(); + params.setServerNames(Collections.singletonList(new SNIHostName(host))); + + var ctx = SSLContext.getInstance("TLS"); + ctx.init(null, new DummyManager[]{new DummyManager()}, new SecureRandom()); + var factory = (SSLSocketFactory) ctx.getSocketFactory(); + + var socket = (SSLSocket) factory.createSocket(host, port); + socket.setSSLParameters(params); + socket.startHandshake(); + return socket; + } + catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new RuntimeException("Unexpected failure", e); + } + } + + public int getCode() { + return code; + } + + public String getMeta() { + return meta; + } + + public BufferedReader body() { + return in; + } + + public void close() throws IOException { + in.close(); + out.close(); + sock.close(); + } +} blob - 765c96e6315075eb5c60a7ed50d61b5930416cb8 blob + be7aae2769a2bfbb000e20794f4bb33d80f36120 --- src/blog/net_gemini.clj +++ src/blog/net_gemini.clj @@ -1,7 +1,7 @@ (ns blog.net-gemini - (:import (com.omarpolo.gemini Gemini))) + (:import (com.omarpolo.gemini Response))) (defn head [host port req] - (with-open [res (Gemini/get host port (str req "\r\n"))] + (with-open [res (Response. host port (str req "\r\n"))] {:code (.getCode res) :meta (.getMeta res)}))