From f152df81ba2fadcb03a7626161c416d577278d9a Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sun, 5 Dec 2021 13:27:58 +0100 Subject: [PATCH 1/6] Implemented a custom http client and added websocket support --- .../javawebstack/httpclient/HTTPClient.java | 27 ++ .../httpclient/HTTPClientSocket.java | 281 ++++++++++++++++++ .../javawebstack/httpclient/HTTPRequest.java | 49 ++- .../httpclient/websocket/WebSocket.java | 96 ++++++ .../httpclient/websocket/WebSocketFrame.java | 153 ++++++++++ .../websocket/WebSocketHandler.java | 13 + 6 files changed, 615 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/javawebstack/httpclient/HTTPClientSocket.java create mode 100644 src/main/java/org/javawebstack/httpclient/websocket/WebSocket.java create mode 100644 src/main/java/org/javawebstack/httpclient/websocket/WebSocketFrame.java create mode 100644 src/main/java/org/javawebstack/httpclient/websocket/WebSocketHandler.java diff --git a/src/main/java/org/javawebstack/httpclient/HTTPClient.java b/src/main/java/org/javawebstack/httpclient/HTTPClient.java index 86bc90b..b109d5c 100644 --- a/src/main/java/org/javawebstack/httpclient/HTTPClient.java +++ b/src/main/java/org/javawebstack/httpclient/HTTPClient.java @@ -3,7 +3,10 @@ import org.javawebstack.abstractdata.AbstractMapper; import org.javawebstack.abstractdata.NamingPolicy; import org.javawebstack.httpclient.interceptor.RequestInterceptor; +import org.javawebstack.httpclient.websocket.WebSocket; +import org.javawebstack.httpclient.websocket.WebSocketHandler; +import java.io.IOException; import java.net.HttpCookie; import java.nio.charset.StandardCharsets; import java.util.*; @@ -27,6 +30,8 @@ public class HTTPClient { private boolean followRedirects = false; + private boolean legacyMode = false; + public HTTPClient(String baseUrl) { this.baseUrl = baseUrl; } @@ -42,6 +47,15 @@ public boolean isDebug(){ return debug; } + public HTTPClient legacyMode() { + this.legacyMode = true; + return this; + } + + public boolean isLegacyMode() { + return legacyMode; + } + public HTTPClient autoCookies(){ autoCookies = true; return this; @@ -156,6 +170,19 @@ public HTTPRequest request(String method, String path){ return new HTTPRequest(this, method, path); } + public WebSocket webSocket(String path, WebSocketHandler handler) throws IOException { + return webSocket(path, handler, null); + } + + public WebSocket webSocket(String path, WebSocketHandler handler, Map additionalHeaders) throws IOException { + HTTPClientSocket socket = new HTTPClientSocket(getBaseUrl() + ((path.startsWith("/") || path.startsWith("http://") || path.startsWith("https://")) ? "" : "/") + path, !isSSLVerification()); + if(additionalHeaders != null) + additionalHeaders.forEach(socket::setRequestHeader); + WebSocket webSocket = new WebSocket(socket, handler); + new Thread(webSocket).start(); + return webSocket; + } + public HTTPRequest get(String path){ return request("GET", path); } diff --git a/src/main/java/org/javawebstack/httpclient/HTTPClientSocket.java b/src/main/java/org/javawebstack/httpclient/HTTPClientSocket.java new file mode 100644 index 0000000..786dd57 --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/HTTPClientSocket.java @@ -0,0 +1,281 @@ +package org.javawebstack.httpclient; + +import javax.net.ssl.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.*; + +public class HTTPClientSocket { + + private final Socket socket; + private final InputStream inputStream; + private InputStream internalInputStream; + private final OutputStream outputStream; + private final String requestPath; + private String requestMethod = "GET"; + private final String host; + private final Map> requestHeaders = new HashMap<>(); + private int responseStatus; + private String responseStatusMessage; + private final Map> responseHeaders = new HashMap<>(); + private boolean headersSent; + private boolean headersReceived; + + public HTTPClientSocket(String url, boolean insecure) throws IOException { + String[] urlSplit = url.split("/", 4); + if(urlSplit.length < 3) + throw new RuntimeException("Invalid HTTP or WebSocket URL: " + url); + boolean ssl = urlSplit[0].equals("https:") || urlSplit[0].equals("wss:"); + this.host = urlSplit[2]; + String[] hostSplit = urlSplit[2].split(":"); + String host = hostSplit[0]; + int port = hostSplit.length > 1 ? Integer.parseInt(hostSplit[1]) : (ssl ? 443 : 80); + requestPath = "/" + (urlSplit.length > 3 ? urlSplit[3] : ""); + if(ssl) { + SSLSocketFactory factory; + if(insecure) { + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + factory = sc.getSocketFactory(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IOException(e); + } + } else { + factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + } + socket = factory.createSocket(host, port); + ((SSLSocket) socket).startHandshake(); + } else { + socket = new Socket(host, port); + } + outputStream = socket.getOutputStream(); + inputStream = socket.getInputStream(); + } + + public InputStream getInputStream() { + return new HTTPInputStream(); + } + + public OutputStream getOutputStream() { + return new HTTPOutputStream(); + } + + public int getResponseStatus() throws IOException { + if(!headersReceived) + readHeaders(); + return responseStatus; + } + + public String getResponseStatusMessage() throws IOException { + if(!headersReceived) + readHeaders(); + return responseStatusMessage; + } + + public HTTPClientSocket setRequestMethod(String method) { + this.requestMethod = method; + return this; + } + + public Set getResponseHeaderNames() throws IOException { + if(!headersReceived) + readHeaders(); + return responseHeaders.keySet(); + } + + public String getResponseHeader(String name) throws IOException { + if(!headersReceived) + readHeaders(); + List values = responseHeaders.get(name.toLowerCase(Locale.ROOT)); + return values == null || values.size() == 0 ? null : values.get(0); + } + + public List getResponseHeaders(String name) throws IOException { + if(!headersReceived) + readHeaders(); + return responseHeaders.getOrDefault(name.toLowerCase(Locale.ROOT), Collections.emptyList()); + } + + public HTTPClientSocket addRequestHeader(String name, String value) { + this.requestHeaders.computeIfAbsent(name.toLowerCase(Locale.ROOT), k -> new ArrayList<>()).add(value); + return this; + } + + public HTTPClientSocket setRequestHeader(String name, String value) { + this.requestHeaders.put(name.toLowerCase(Locale.ROOT), Arrays.asList(value)); + return this; + } + + private void writeHeaders() throws IOException { + if(headersSent) + return; + headersSent = true; + StringBuilder sb = new StringBuilder(requestMethod.toUpperCase(Locale.ROOT)) + .append(" ") + .append(requestPath) + .append(" HTTP/1.1\nHost: ") + .append(host) + .append("\r\nHost: ") + .append(host); + sb.append("\r\n"); + requestHeaders.forEach((k, values) -> values.forEach(v -> sb.append(k).append(": ").append(v).append("\r\n"))); + sb.append("\r\n"); + outputStream.write(sb.toString().getBytes(StandardCharsets.UTF_8)); + } + + private void readHeaders() throws IOException { + if(headersReceived) + return; + if(!headersSent) + writeHeaders(); + headersReceived = true; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int lb = -1; + while (true) { + int b = inputStream.read(); + if(b == -1) { + socket.close(); + throw new IOException("Unexpected end of stream"); + } + if(b == '\r' && lb == '\n') { + b = inputStream.read(); + break; + } + baos.write(b); + lb = b; + } + String[] lines = new String(baos.toByteArray(), StandardCharsets.UTF_8).split("\\r?\\n"); + if(lines.length < 2) { + socket.close(); + throw new IOException("Invalid http response"); + } + String[] first = lines[0].split(" ", 3); + if(first.length != 3 || !first[0].startsWith("HTTP/")) { + socket.close(); + throw new IOException("Invalid http response"); + } + responseStatus = Integer.parseInt(first[1]); + responseStatusMessage = first[2]; + for(int i=1; i values = responseHeaders.computeIfAbsent(hspl[0].toLowerCase(Locale.ROOT), h -> new ArrayList<>()); + values.add(hspl[1]); + } + if(getResponseHeader("transfer-encoding") != null) { + switch (getResponseHeader("transfer-encoding")) { + case "chunked": + internalInputStream = new ChunkedHTTPInputStream(); + break; + default: + internalInputStream = new StandardHTTPInputStream(); + break; + } + } else if(getResponseHeader("upgrade") != null) { + internalInputStream = inputStream; + } else { + internalInputStream = new StandardHTTPInputStream(); + } + } + + public void close() throws IOException { + socket.close(); + } + + public boolean isClosed() { + return socket.isClosed(); + } + + private class StandardHTTPInputStream extends InputStream { + int len; + int c = 0; + StandardHTTPInputStream() throws IOException { + String cl = getResponseHeader("content-length"); + len = cl == null ? 0 : Integer.parseInt(cl); + } + public int read() throws IOException { + if(c >= len) + return -1; + c++; + return inputStream.read(); + } + } + + private class ChunkedHTTPInputStream extends InputStream { + int remChunk = -1; + boolean finished; + public int read() throws IOException { + if(finished) + return -1; + if(remChunk < 1) { + if(remChunk == 0) { + inputStream.read(); + inputStream.read(); + } + int b; + StringBuilder hex = new StringBuilder(); + while ((b = inputStream.read()) != '\r') + hex.append((char) b); + remChunk = Integer.parseInt(hex.toString(), 16); + if(remChunk == 0) { + finished = true; + return -1; + } + inputStream.read(); + } + remChunk--; + return inputStream.read(); + } + } + + private class HTTPInputStream extends InputStream { + public int available() throws IOException { + if(!headersReceived) + readHeaders(); + return internalInputStream.available(); + } + public int read() throws IOException { + if(!headersReceived) + readHeaders(); + return internalInputStream.read(); + } + } + + private class HTTPOutputStream extends OutputStream { + public void flush() throws IOException { + writeHeaders(); + super.flush(); + } + public void write(int i) throws IOException { + if(!headersSent) + writeHeaders(); + outputStream.write(i); + } + public void close() throws IOException { + close(); + } + } + +} diff --git a/src/main/java/org/javawebstack/httpclient/HTTPRequest.java b/src/main/java/org/javawebstack/httpclient/HTTPRequest.java index 4716dc1..ee23916 100644 --- a/src/main/java/org/javawebstack/httpclient/HTTPRequest.java +++ b/src/main/java/org/javawebstack/httpclient/HTTPRequest.java @@ -192,9 +192,50 @@ public String[] headers(String key) { public HTTPRequest execute() { if (responseBody != null) return this; + if(client.isLegacyMode()) { + executeWithHttpUrlConnection(); + } else { + executeWithHTTPClientSocket(); + } + return this; + } + + private String buildUrl() { + return client.getBaseUrl() + ((path.startsWith("/") || path.startsWith("http://") || path.startsWith("https://")) ? "" : "/") + path + (query.size() > 0 ? "?" + query.toString() : ""); + } + + private void executeWithHTTPClientSocket() { + try { + HTTPClientSocket socket = new HTTPClientSocket(buildUrl(), !client.isSSLVerification()); + socket.setRequestMethod(method); + requestHeaders.forEach((k, values) -> { + for(String v : values) + socket.addRequestHeader(k ,v); + }); + if(requestBody != null) { + socket.setRequestHeader("content-length", String.valueOf(requestBody.length)); + socket.getOutputStream().write(requestBody); + } + status = socket.getResponseStatus(); + for(String k : socket.getResponseHeaderNames()) + responseHeaders.put(k, socket.getResponseHeaders(k).toArray(new String[0])); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int r; + while ((r = socket.getInputStream().read(buffer)) != -1) + baos.write(buffer, 0, r); + socket.close(); + responseBody = baos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private void executeWithHttpUrlConnection() { HttpURLConnection conn = null; try{ - URL theUrl = new URL(client.getBaseUrl() + ((path.startsWith("/") || path.startsWith("http://") || path.startsWith("https://")) ? "" : "/") + path + (query.size() > 0 ? "?" + query.toString() : "")); + URL theUrl = new URL(buildUrl()); conn = (HttpURLConnection) theUrl.openConnection(); if(!client.isSSLVerification() && conn instanceof HttpsURLConnection){ HttpsURLConnection httpsConn = (HttpsURLConnection) conn; @@ -269,7 +310,7 @@ public void checkServerTrusted(X509Certificate[] certs, String authType) { if (client.getAfterInterceptor() != null) client.getAfterInterceptor().intercept(this); - return this; + return; }catch(Exception e){ try { status = conn.getResponseCode(); @@ -278,14 +319,14 @@ public void checkServerTrusted(X509Certificate[] certs, String authType) { e.printStackTrace(); try { this.responseBody = readAll(conn.getErrorStream()); - return this; + return; }catch(IOException | NullPointerException ex){ if(client.isDebug()) ex.printStackTrace(); } } this.responseBody = new byte[0]; - return this; + return; } private static byte[] readAll(InputStream is) throws IOException { diff --git a/src/main/java/org/javawebstack/httpclient/websocket/WebSocket.java b/src/main/java/org/javawebstack/httpclient/websocket/WebSocket.java new file mode 100644 index 0000000..9e37c32 --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/websocket/WebSocket.java @@ -0,0 +1,96 @@ +package org.javawebstack.httpclient.websocket; + +import org.javawebstack.httpclient.HTTPClientSocket; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Base64; + +public class WebSocket implements Runnable { + + private final HTTPClientSocket socket; + private final WebSocketHandler handler; + + public WebSocket(HTTPClientSocket socket, WebSocketHandler handler) throws IOException { + this.socket = socket; + this.handler = handler; + socket.setRequestHeader("connection", "Upgrade"); + socket.setRequestHeader("upgrade", "websocket"); + byte[] keyBytes = new byte[16]; + new SecureRandom().nextBytes(keyBytes); + String key = new String(Base64.getEncoder().encode(keyBytes), StandardCharsets.US_ASCII); + socket.setRequestHeader("sec-websocket-key", key); + socket.setRequestHeader("sec-websocket-version", "13"); + if(socket.getResponseStatus() != 101) + throw new IOException("Server didn't accept protocol change"); + handler.onOpen(this); + } + + public boolean isClosed() { + return socket.isClosed(); + } + + public void run() { + WebSocketFrame frame; + while (!socket.isClosed()) { + try { + frame = WebSocketFrame.read(socket.getInputStream()); + switch (frame.getOpcode()) { + case WebSocketFrame.OP_CLOSE: + Integer code = null; + String reason = null; + byte[] payload = frame.getPayload(); + if(payload.length >= 2) { + code = (payload[0] << 8) | payload[1]; + if(payload.length > 2) { + byte[] reasonBytes = new byte[payload.length - 2]; + System.arraycopy(payload, 2, reasonBytes, 0, reasonBytes.length); + reason = new String(reasonBytes, StandardCharsets.UTF_8); + } + } + handler.onClose(this, code, reason); + frame.setMaskKey().write(socket.getOutputStream()); + socket.close(); + break; + case WebSocketFrame.OP_PING: + frame.setOpcode(WebSocketFrame.OP_PONG).setMaskKey().write(socket.getOutputStream()); + break; + case WebSocketFrame.OP_BINARY: + handler.onMessage(this, frame.getPayload()); + break; + case WebSocketFrame.OP_TEXT: + handler.onMessage(this, new String(frame.getPayload(), StandardCharsets.UTF_8)); + break; + } + } catch (IOException e) { + handler.onClose(this, null, e.getMessage()); + return; + } + } + } + + public void send(byte[] message) throws IOException { + new WebSocketFrame().setFin(true).setOpcode(WebSocketFrame.OP_BINARY).setPayload(message).setMaskKey().write(socket.getOutputStream()); + } + + public void send(String message) throws IOException { + new WebSocketFrame().setFin(true).setOpcode(WebSocketFrame.OP_TEXT).setPayload(message.getBytes(StandardCharsets.UTF_8)).setMaskKey().write(socket.getOutputStream()); + } + + public void close(Integer code, String reason) { + byte[] reasonBytes = reason == null ? null : reason.getBytes(StandardCharsets.UTF_8); + byte[] payload = new byte[code == null ? 0 : (reason == null ? 2 : (reasonBytes.length + 2))]; + if(code != null) { + payload[0] = (byte) (code >> 8); + payload[1] = (byte) (code & 0xF); + if(reasonBytes != null) + System.arraycopy(reasonBytes, 0, payload, 2, reasonBytes.length); + } + try { + new WebSocketFrame().setFin(true).setOpcode(WebSocketFrame.OP_CLOSE).setPayload(payload).setMaskKey().write(socket.getOutputStream()); + socket.close(); + } catch (IOException ignored) {} + } + +} diff --git a/src/main/java/org/javawebstack/httpclient/websocket/WebSocketFrame.java b/src/main/java/org/javawebstack/httpclient/websocket/WebSocketFrame.java new file mode 100644 index 0000000..ad0c778 --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/websocket/WebSocketFrame.java @@ -0,0 +1,153 @@ +package org.javawebstack.httpclient.websocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; + +public class WebSocketFrame { + + public static final byte OP_CLOSE = 0x8; + public static final byte OP_PING = 0x9; + public static final byte OP_PONG = 0xA; + public static final byte OP_TEXT = 0x1; + public static final byte OP_BINARY = 0x2; + + private byte flags; + private byte opcode; + private byte[] maskKey; + private byte[] payload; + + public boolean isFin() { + return (flags & 0b1000_0000) > 0; + } + + public WebSocketFrame setFin(boolean fin) { + flags = (byte) ((flags & 0b0111_1111) | ((fin ? 1 : 0) << 7)); + return this; + } + + public boolean isRsv1() { + return (flags & 0b0100_0000) > 0; + } + + public WebSocketFrame setRsv1(boolean rsv1) { + flags = (byte) ((flags & 0b1011_1111) | ((rsv1 ? 1 : 0) << 6)); + return this; + } + + public boolean isRsv2() { + return (flags & 0b0010_0000) > 0; + } + + public WebSocketFrame setRsv2(boolean rsv2) { + flags = (byte) ((flags & 0b1101_1111) | ((rsv2 ? 1 : 0) << 5)); + return this; + } + + public boolean isRsv3() { + return (flags & 0b0001_0000) > 0; + } + + public WebSocketFrame setRsv3(boolean rsv3) { + flags = (byte) ((flags & 0b1110_1111) | ((rsv3 ? 1 : 0) << 4)); + return this; + } + + public byte getOpcode() { + return opcode; + } + + public WebSocketFrame setOpcode(byte opcode) { + this.opcode = opcode; + return this; + } + + public byte[] getMaskKey() { + return maskKey; + } + + public WebSocketFrame setMaskKey(byte[] maskKey) { + this.maskKey = maskKey; + return this; + } + + public WebSocketFrame setMaskKey() { + byte[] key = new byte[4]; + new SecureRandom().nextBytes(key); + return setMaskKey(key); + } + + public byte[] getPayload() { + return payload; + } + + public WebSocketFrame setPayload(byte[] payload) { + this.payload = payload; + return this; + } + + public void write(OutputStream stream) throws IOException { + stream.write(flags | opcode); + int lengthByte = payload.length > 125 ? (payload.length > 0xFFFF ? 127 : 126) : payload.length; + stream.write((maskKey != null ? 0b1000_0000 : 0) | lengthByte); + if(lengthByte == 127) { + stream.write(payload.length >> 24); + stream.write((payload.length & 0xFF0000) >> 16); + } + if(lengthByte > 125) { + stream.write((payload.length & 0xFF00) >> 8); + stream.write(payload.length & 0xFF); + } + if(maskKey != null) + stream.write(maskKey); + if(maskKey != null) { + for(int i=0; i> 7) == 1 ? new byte[4] : null; + int len = b & 0b0111_1111; + if(len == 126) { + len = safeRead(stream) << 8; + len |= safeRead(stream); + } else if(len == 127) { + len = safeRead(stream) << 24; + len |= safeRead(stream) << 16; + len |= safeRead(stream) << 8; + len |= safeRead(stream); + } + if(frame.maskKey != null) { + frame.maskKey[0] = (byte) safeRead(stream); + frame.maskKey[1] = (byte) safeRead(stream); + frame.maskKey[2] = (byte) safeRead(stream); + frame.maskKey[3] = (byte) safeRead(stream); + } + frame.payload = new byte[len]; + if(frame.maskKey != null) { + for(int i=0; i Date: Sun, 5 Dec 2021 13:29:59 +0100 Subject: [PATCH 2/6] Upgraded junit and removed gson --- pom.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 0e92285..5b6de51 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 8 - 1.0.1-SNAPSHOT + 1.0.2-SNAPSHOT org.javawebstack @@ -15,7 +15,7 @@ ${buildVersion} http-client - A simple to use and extendable http client wrapper around java's built in HttpURLConnection. + A simple to use and extendable http client https://github.com/JavaWebStack/http-client @@ -41,11 +41,6 @@ - - com.google.code.gson - gson - 2.8.9 - org.javawebstack abstract-data @@ -54,7 +49,7 @@ org.junit.jupiter junit-jupiter-engine - 5.8.1 + 5.8.2 test From e12b53fd1b02e71b69d60853be08dcf56e150ee4 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sun, 5 Dec 2021 13:31:02 +0100 Subject: [PATCH 3/6] Upgraded junit and removed gson --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5b6de51..b6e8197 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 8 - 1.0.2-SNAPSHOT + 1.0.1-SNAPSHOT org.javawebstack From c78288fa997e9a4de2e0cea068089a3de3914549 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 18 Jan 2022 23:16:04 +0100 Subject: [PATCH 4/6] Abstracted the http implementation and added support for using Apache HTTP Client --- pom.xml | 6 + .../javawebstack/httpclient/HTTPClient.java | 77 ++++--- .../javawebstack/httpclient/HTTPRequest.java | 208 +++++------------- .../ApacheHTTPRequestImplementation.java | 132 +++++++++++ .../IHTTPRequestImplementation.java | 30 +++ .../JavaNetHTTPRequestImplementation.java | 138 ++++++++++++ .../SimpleHTTPRequestImplementation.java | 93 ++++++++ 7 files changed, 499 insertions(+), 185 deletions(-) create mode 100644 src/main/java/org/javawebstack/httpclient/implementation/ApacheHTTPRequestImplementation.java create mode 100644 src/main/java/org/javawebstack/httpclient/implementation/IHTTPRequestImplementation.java create mode 100644 src/main/java/org/javawebstack/httpclient/implementation/JavaNetHTTPRequestImplementation.java create mode 100644 src/main/java/org/javawebstack/httpclient/implementation/SimpleHTTPRequestImplementation.java diff --git a/pom.xml b/pom.xml index b6e8197..1567aea 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,12 @@ 5.8.2 test + + org.apache.httpcomponents + httpclient + 4.5.13 + true + diff --git a/src/main/java/org/javawebstack/httpclient/HTTPClient.java b/src/main/java/org/javawebstack/httpclient/HTTPClient.java index b109d5c..217139a 100644 --- a/src/main/java/org/javawebstack/httpclient/HTTPClient.java +++ b/src/main/java/org/javawebstack/httpclient/HTTPClient.java @@ -2,6 +2,8 @@ import org.javawebstack.abstractdata.AbstractMapper; import org.javawebstack.abstractdata.NamingPolicy; +import org.javawebstack.httpclient.implementation.IHTTPRequestImplementation; +import org.javawebstack.httpclient.implementation.JavaNetHTTPRequestImplementation; import org.javawebstack.httpclient.interceptor.RequestInterceptor; import org.javawebstack.httpclient.websocket.WebSocket; import org.javawebstack.httpclient.websocket.WebSocketHandler; @@ -10,17 +12,20 @@ import java.net.HttpCookie; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.function.Supplier; public class HTTPClient { private AbstractMapper abstractMapper = new AbstractMapper() .setNamingPolicy(NamingPolicy.SNAKE_CASE); private int timeout = 5000; - private String baseUrl = ""; + private String baseUrl; private Map defaultHeaders = new HashMap<>(); private Map defaultQuery = new HashMap<>(); private List defaultCookies = new ArrayList<>(); + private Supplier httpImplementation = JavaNetHTTPRequestImplementation::new; + private RequestInterceptor beforeInterceptor; private RequestInterceptor afterInterceptor; @@ -30,60 +35,60 @@ public class HTTPClient { private boolean followRedirects = false; - private boolean legacyMode = false; - public HTTPClient(String baseUrl) { this.baseUrl = baseUrl; } - public HTTPClient() { } + public HTTPClient() { + this(""); + } - public HTTPClient debug(){ + public HTTPClient debug() { this.debug = true; return this; } - public boolean isDebug(){ + public boolean isDebug() { return debug; } - public HTTPClient legacyMode() { - this.legacyMode = true; - return this; + public Supplier getHttpImplementation() { + return httpImplementation; } - public boolean isLegacyMode() { - return legacyMode; + public HTTPClient httpImplementation(Supplier implementation) { + this.httpImplementation = implementation; + return this; } - public HTTPClient autoCookies(){ + public HTTPClient autoCookies() { autoCookies = true; return this; } - public boolean isAutoCookies(){ + public boolean isAutoCookies() { return autoCookies; } - public HTTPClient setSSLVerification(boolean sslVerification){ + public HTTPClient setSSLVerification(boolean sslVerification) { this.sslVerification = sslVerification; return this; } - public boolean isSSLVerification(){ + public boolean isSSLVerification() { return this.sslVerification; } - public HTTPClient abstractMapper(AbstractMapper mapper){ + public HTTPClient abstractMapper(AbstractMapper mapper) { this.abstractMapper = mapper; return this; } - public AbstractMapper getAbstractMapper(){ + public AbstractMapper getAbstractMapper() { return abstractMapper; } - public HTTPClient timeout(int timeout){ + public HTTPClient timeout(int timeout) { this.timeout = timeout; return this; } @@ -92,35 +97,35 @@ public int getTimeout() { return timeout; } - public HTTPClient header(String key, String... values){ + public HTTPClient header(String key, String... values) { defaultHeaders.put(key, values); return this; } - public HTTPClient query(String key, String value){ + public HTTPClient query(String key, String value) { defaultQuery.put(key, value); return this; } - public HTTPClient cookie(HttpCookie cookie){ + public HTTPClient cookie(HttpCookie cookie) { removeCookie(cookie.getName()); defaultCookies.add(cookie); return this; } - public HTTPClient removeCookie(String name){ - for(HttpCookie cookie : new HashSet<>(defaultCookies)){ + public HTTPClient removeCookie(String name) { + for(HttpCookie cookie : new HashSet<>(defaultCookies)) { if(cookie.getName().equalsIgnoreCase(name)) defaultCookies.remove(cookie); } return this; } - public List getDefaultCookies(){ + public List getDefaultCookies() { return defaultCookies; } - public HTTPClient setDefaultCookies(List cookies){ + public HTTPClient setDefaultCookies(List cookies) { this.defaultCookies = cookies; return this; } @@ -138,16 +143,16 @@ public HTTPClient setDefaultQuery(Map defaultQuery) { return this; } - public HTTPClient setDefaultHeaders(Map defaultHeaders){ + public HTTPClient setDefaultHeaders(Map defaultHeaders) { this.defaultHeaders = defaultHeaders; return this; } - public HTTPClient authorization(String type, String value){ + public HTTPClient authorization(String type, String value) { return header("Authorization", type + " " + value); } - public HTTPClient bearer(String token){ + public HTTPClient bearer(String token) { return authorization("Bearer", token); } @@ -166,7 +171,7 @@ public String getBaseUrl() { return baseUrl; } - public HTTPRequest request(String method, String path){ + public HTTPRequest request(String method, String path) { return new HTTPRequest(this, method, path); } @@ -183,11 +188,11 @@ public WebSocket webSocket(String path, WebSocketHandler handler, Map requestHeaders = new HashMap<>(); private byte[] requestBody; - private final Map responseHeaders = new HashMap<>(); + private Map responseHeaders = new HashMap<>(); private final List requestCookies = new ArrayList<>(); private final List responseCookies = new ArrayList<>(); private byte[] responseBody; private int status; + private boolean executed; private boolean followRedirects; - public HTTPRequest(HTTPClient client, String method, String path){ + public HTTPRequest(HTTPClient client, String method, String path) { this.client = client; + this.method = method; this.path = path; this.followRedirects = client.isFollowingRedirects(); @@ -40,12 +40,12 @@ public HTTPRequest(HTTPClient client, String method, String path){ header(key, client.getDefaultHeaders().get(key)); } - public HTTPRequest cookie(HttpCookie cookie){ + public HTTPRequest cookie(HttpCookie cookie) { requestCookies.add(cookie); return this; } - public List cookies(){ + public List cookies() { return responseCookies; } @@ -53,8 +53,8 @@ public HttpCookie cookie(String name) { return responseCookies.stream().filter(c -> c.getName().equalsIgnoreCase(name)).findFirst().orElse(null); } - public HTTPRequest header(String key, String... values){ - requestHeaders.put(key, values); + public HTTPRequest header(String key, String... values) { + requestHeaders.put(key.toLowerCase(Locale.ROOT), values); return this; } @@ -63,28 +63,28 @@ public HTTPRequest query(String key, String value){ return this; } - public HTTPRequest query(String key1, String key2, String value){ + public HTTPRequest query(String key1, String key2, String value) { return query(key1 + "[" + key2 + "]", value); } - public HTTPRequest query(String key1, String key2, String key3, String value){ + public HTTPRequest query(String key1, String key2, String key3, String value) { return query(key1 + "[" + key2 + "]" + "[" + key3 + "]", value); } - public HTTPRequest body(byte[] body){ + public HTTPRequest body(byte[] body) { this.requestBody = body; return this; } - public HTTPRequest body(String body){ + public HTTPRequest body(String body) { return body(body.getBytes(StandardCharsets.UTF_8)); } - public HTTPRequest contentType(String contentType){ + public HTTPRequest contentType(String contentType) { return header("Content-Type", contentType); } - public HTTPRequest authorization(String type, String value){ + public HTTPRequest authorization(String type, String value) { return header("Authorization", type + " " + value); } @@ -112,11 +112,11 @@ public HTTPRequest formBodyElement(AbstractElement element) { return body(element.toFormDataString()).contentType("application/x-www-form-urlencoded"); } - public HTTPRequest bearer(String token){ + public HTTPRequest bearer(String token) { return authorization("Bearer", token); } - public HTTPRequest tokenAuth(String token){ + public HTTPRequest tokenAuth(String token) { return authorization("token", token); } @@ -144,11 +144,11 @@ public byte[] bytes() { return responseBody; } - public String string(){ + public String string() { return new String(bytes(), StandardCharsets.UTF_8); } - public String redirect(){ + public String redirect() { return header("Location"); } @@ -190,143 +190,53 @@ public String[] headers(String key) { } public HTTPRequest execute() { - if (responseBody != null) + if (executed) return this; - if(client.isLegacyMode()) { - executeWithHttpUrlConnection(); - } else { - executeWithHTTPClientSocket(); + executed = true; + + if (client.getBeforeInterceptor() != null) + client.getBeforeInterceptor().intercept(this); + + List reqCookies = new ArrayList<>(); + for(HttpCookie cookie : client.getDefaultCookies()) + reqCookies.add(cookie.getName()+"="+cookie.getValue()); + for(HttpCookie cookie : requestCookies) + reqCookies.add(cookie.getName()+"="+cookie.getValue()); + + if(reqCookies.size() > 0) { + String[] headers = requestHeaders.get("cookie"); + if(headers == null) { + requestHeaders.put("cookie", new String[]{ String.join("; ", reqCookies) }); + } else { + String[] newHeaders = new String[headers.length + 1]; + System.arraycopy(headers, 0, newHeaders, 0, headers.length); + newHeaders[newHeaders.length - 1] = String.join("; ", reqCookies); + requestHeaders.put("cookie", newHeaders); + } } - return this; - } - - private String buildUrl() { - return client.getBaseUrl() + ((path.startsWith("/") || path.startsWith("http://") || path.startsWith("https://")) ? "" : "/") + path + (query.size() > 0 ? "?" + query.toString() : ""); - } - private void executeWithHTTPClientSocket() { + IHTTPRequestImplementation requestImplementation = client.getHttpImplementation().get(); + requestImplementation.setUrl(buildUrl()); + requestImplementation.setMethod(method); + requestImplementation.setTimeout(client.getTimeout()); + requestImplementation.setFollowRedirects(followRedirects); + requestImplementation.setRequestHeaders(requestHeaders); + requestImplementation.setSslVerification(client.isSSLVerification()); + requestImplementation.setRequestBody(requestBody); + status = requestImplementation.execute(); + responseHeaders = requestImplementation.getResponseHeaders(); try { - HTTPClientSocket socket = new HTTPClientSocket(buildUrl(), !client.isSSLVerification()); - socket.setRequestMethod(method); - requestHeaders.forEach((k, values) -> { - for(String v : values) - socket.addRequestHeader(k ,v); - }); - if(requestBody != null) { - socket.setRequestHeader("content-length", String.valueOf(requestBody.length)); - socket.getOutputStream().write(requestBody); - } - status = socket.getResponseStatus(); - for(String k : socket.getResponseHeaderNames()) - responseHeaders.put(k, socket.getResponseHeaders(k).toArray(new String[0])); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int r; - while ((r = socket.getInputStream().read(buffer)) != -1) - baos.write(buffer, 0, r); - socket.close(); - responseBody = baos.toByteArray(); - } catch (IOException e) { - e.printStackTrace(); - } + responseBody = readAll(requestImplementation.getResponseStream()); + requestImplementation.close(); + } catch (IOException ignored) {} + if (client.getAfterInterceptor() != null) + client.getAfterInterceptor().intercept(this); + return this; } - private void executeWithHttpUrlConnection() { - HttpURLConnection conn = null; - try{ - URL theUrl = new URL(buildUrl()); - conn = (HttpURLConnection) theUrl.openConnection(); - if(!client.isSSLVerification() && conn instanceof HttpsURLConnection){ - HttpsURLConnection httpsConn = (HttpsURLConnection) conn; - TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } - }; - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - httpsConn.setSSLSocketFactory(sc.getSocketFactory()); - httpsConn.setHostnameVerifier((hostname, session) -> true); - } - conn.setReadTimeout(client.getTimeout()); - conn.setConnectTimeout(5000); - conn.setRequestMethod(method); - conn.setDoInput(true); - conn.setInstanceFollowRedirects(followRedirects); - for(String k : requestHeaders.keySet()){ - for(String v : requestHeaders.get(k)) - conn.addRequestProperty(k, v); - } - - List reqCookies = new ArrayList<>(); - for(HttpCookie cookie : client.getDefaultCookies()) - reqCookies.add(cookie.getName()+"="+cookie.getValue()); - for(HttpCookie cookie : requestCookies) - reqCookies.add(cookie.getName()+"="+cookie.getValue()); - - if(reqCookies.size() > 0) - conn.addRequestProperty("Cookie", String.join("; ", reqCookies)); - - if (client.getBeforeInterceptor() != null) - client.getBeforeInterceptor().intercept(this); - - - if(requestBody!=null){ - conn.setDoOutput(true); - OutputStream os = conn.getOutputStream(); - os.write(requestBody); - os.flush(); - os.close(); - } - - status = conn.getResponseCode(); - - conn.getHeaderFields().forEach((k,v) -> { - if(k != null && v != null) - responseHeaders.put(k.toLowerCase(Locale.ROOT), v.toArray(new String[0])); - }); - - for(String value : headers("set-cookie")) - responseCookies.addAll(HttpCookie.parse("set-cookie: "+value)); - for(String value : headers("set-cookie2")) - responseCookies.addAll(HttpCookie.parse("set-cookie2: "+value)); - if(client.isAutoCookies()) - cookies().forEach(client::cookie); - - if(status>299 || status<200){ - this.responseBody = readAll(conn.getErrorStream()); - }else{ - this.responseBody = readAll(conn.getInputStream()); - } - - - if (client.getAfterInterceptor() != null) - client.getAfterInterceptor().intercept(this); - - return; - }catch(Exception e){ - try { - status = conn.getResponseCode(); - } catch (IOException ioException) {} - if(client.isDebug()) - e.printStackTrace(); - try { - this.responseBody = readAll(conn.getErrorStream()); - return; - }catch(IOException | NullPointerException ex){ - if(client.isDebug()) - ex.printStackTrace(); - } - } - this.responseBody = new byte[0]; - return; + private String buildUrl() { + return client.getBaseUrl() + ((path.startsWith("/") || path.startsWith("http://") || path.startsWith("https://")) ? "" : "/") + path + (query.size() > 0 ? "?" + query.toString() : ""); } private static byte[] readAll(InputStream is) throws IOException { diff --git a/src/main/java/org/javawebstack/httpclient/implementation/ApacheHTTPRequestImplementation.java b/src/main/java/org/javawebstack/httpclient/implementation/ApacheHTTPRequestImplementation.java new file mode 100644 index 0000000..c3cdb58 --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/implementation/ApacheHTTPRequestImplementation.java @@ -0,0 +1,132 @@ +package org.javawebstack.httpclient.implementation; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.ssl.SSLContextBuilder; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +public class ApacheHTTPRequestImplementation implements IHTTPRequestImplementation { + + private String method; + private String url; + private boolean sslVerification; + private boolean followRedirects; + private int timeout; + private Map requestHeaders; + private byte[] requestBody; + + private int status; + private Map responseHeaders = new HashMap<>(); + private HttpEntity responseEntity; + + public void setMethod(String method) { + this.method = method; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setSslVerification(boolean sslVerification) { + this.sslVerification = sslVerification; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void setRequestHeaders(Map requestHeaders) { + this.requestHeaders = requestHeaders; + } + + public void setRequestBody(byte[] requestBody) { + this.requestBody = requestBody; + } + + public int getResponseStatus() { + return status; + } + + public Map getResponseHeaders() { + return responseHeaders; + } + + public InputStream getResponseStream() { + try { + return responseEntity.getContent(); + } catch (IOException ignored) { + return null; + } + } + + public int execute() { + try { + RequestConfig config = RequestConfig.custom() + .setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setRedirectsEnabled(followRedirects) + .build(); + HttpClientBuilder clientBuilder = HttpClientBuilder.create() + .setDefaultRequestConfig(config); + + if(!sslVerification) { + clientBuilder + .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build()) + .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + } + + HttpClient client = clientBuilder.build(); + + RequestBuilder builder = RequestBuilder.create(method); + builder.setUri(url); + requestHeaders.forEach((k, values) -> { + for(String v : values) + builder.addHeader(k ,v); + }); + + if(requestBody != null) { + String contentType = requestHeaders.computeIfAbsent("content-type", n -> new String[]{ "text/plain" })[0]; + builder.setEntity(new ByteArrayEntity(requestBody, ContentType.create(contentType))); + } + + HttpResponse response = client.execute(builder.build()); + + status = response.getStatusLine().getStatusCode(); + Map> resHeaders = new HashMap<>(); + for(Header h : response.getAllHeaders()) + resHeaders.computeIfAbsent(h.getName().toLowerCase(Locale.ROOT), n -> new ArrayList<>()).add(h.getValue()); + resHeaders.forEach((k, v) -> responseHeaders.put(k, v.toArray(new String[0]))); + + responseEntity = response.getEntity(); + } catch (IOException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { + + } + if(status == 0) + status = -1; + return status; + } + + public void close() { + + } + +} diff --git a/src/main/java/org/javawebstack/httpclient/implementation/IHTTPRequestImplementation.java b/src/main/java/org/javawebstack/httpclient/implementation/IHTTPRequestImplementation.java new file mode 100644 index 0000000..10149a0 --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/implementation/IHTTPRequestImplementation.java @@ -0,0 +1,30 @@ +package org.javawebstack.httpclient.implementation; + +import java.io.InputStream; +import java.util.Map; + +public interface IHTTPRequestImplementation { + + void setMethod(String method); + + void setUrl(String url); + + void setSslVerification(boolean sslVerification); + + void setFollowRedirects(boolean followRedirects); + + void setTimeout(int timeout); + + void setRequestHeaders(Map requestHeaders); + + void setRequestBody(byte[] requestBody); + + Map getResponseHeaders(); + + InputStream getResponseStream(); + + int execute(); + + void close(); + +} diff --git a/src/main/java/org/javawebstack/httpclient/implementation/JavaNetHTTPRequestImplementation.java b/src/main/java/org/javawebstack/httpclient/implementation/JavaNetHTTPRequestImplementation.java new file mode 100644 index 0000000..b7a22d2 --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/implementation/JavaNetHTTPRequestImplementation.java @@ -0,0 +1,138 @@ +package org.javawebstack.httpclient.implementation; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public class JavaNetHTTPRequestImplementation implements IHTTPRequestImplementation { + + private String method; + private String url; + private HttpURLConnection conn; + private boolean sslVerification; + private boolean followRedirects; + private int timeout; + private Map requestHeaders; + private byte[] requestBody; + + private int status; + + public void setMethod(String method) { + this.method = method; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setSslVerification(boolean sslVerification) { + this.sslVerification = sslVerification; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void setRequestHeaders(Map requestHeaders) { + this.requestHeaders = requestHeaders; + } + + public void setRequestBody(byte[] requestBody) { + this.requestBody = requestBody; + } + + public int getResponseStatus() { + return status; + } + + public Map getResponseHeaders() { + Map responseHeaders = new HashMap<>(); + conn.getHeaderFields().forEach((k,v) -> { + if(k != null && v != null) + responseHeaders.put(k.toLowerCase(Locale.ROOT), v.toArray(new String[0])); + }); + return responseHeaders; + } + + public InputStream getResponseStream() { + int status = getResponseStatus(); + try { + if(status>299 || status<200){ + return conn.getErrorStream(); + }else{ + return conn.getInputStream(); + } + } catch (IOException ignored) { + return null; + } + } + + public int execute() { + try{ + URL theUrl = new URL(url); + conn = (HttpURLConnection) theUrl.openConnection(); + if(!sslVerification && conn instanceof HttpsURLConnection){ + HttpsURLConnection httpsConn = (HttpsURLConnection) conn; + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + httpsConn.setSSLSocketFactory(sc.getSocketFactory()); + httpsConn.setHostnameVerifier((hostname, session) -> true); + } + + conn.setReadTimeout(timeout); + conn.setConnectTimeout(timeout); + conn.setRequestMethod(method); + conn.setDoInput(true); + conn.setInstanceFollowRedirects(followRedirects); + + for(String k : requestHeaders.keySet()){ + for(String v : requestHeaders.get(k)) + conn.addRequestProperty(k, v); + } + + if(requestBody != null){ + conn.setDoOutput(true); + OutputStream os = conn.getOutputStream(); + os.write(requestBody); + os.flush(); + os.close(); + } + + status = conn.getResponseCode(); + + }catch(Exception ignored) {} + if(status == 0) + status = -1; + return status; + } + + public void close() { + + } + +} diff --git a/src/main/java/org/javawebstack/httpclient/implementation/SimpleHTTPRequestImplementation.java b/src/main/java/org/javawebstack/httpclient/implementation/SimpleHTTPRequestImplementation.java new file mode 100644 index 0000000..1f67f6e --- /dev/null +++ b/src/main/java/org/javawebstack/httpclient/implementation/SimpleHTTPRequestImplementation.java @@ -0,0 +1,93 @@ +package org.javawebstack.httpclient.implementation; + +import org.javawebstack.httpclient.HTTPClientSocket; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +public class SimpleHTTPRequestImplementation implements IHTTPRequestImplementation { + + private String method; + private String url; + private boolean sslVerification; + private boolean followRedirects; + private int timeout; + private Map requestHeaders; + private byte[] requestBody; + + private int status; + private Map responseHeaders = new HashMap<>(); + private HTTPClientSocket socket; + + public void setMethod(String method) { + this.method = method; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setSslVerification(boolean sslVerification) { + this.sslVerification = sslVerification; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void setRequestHeaders(Map requestHeaders) { + this.requestHeaders = requestHeaders; + } + + public void setRequestBody(byte[] requestBody) { + this.requestBody = requestBody; + } + + public int getResponseStatus() { + return status; + } + + public Map getResponseHeaders() { + return responseHeaders; + } + + public InputStream getResponseStream() { + return socket.getInputStream(); + } + + public int execute() { + try { + socket = new HTTPClientSocket(url, !sslVerification); + socket.setRequestMethod(method); + requestHeaders.forEach((k, values) -> { + for(String v : values) + socket.addRequestHeader(k ,v); + }); + if(requestBody != null) { + socket.setRequestHeader("content-length", String.valueOf(requestBody.length)); + socket.getOutputStream().write(requestBody); + } + status = socket.getResponseStatus(); + for(String k : socket.getResponseHeaderNames()) + responseHeaders.put(k, socket.getResponseHeaders(k).toArray(new String[0])); + } catch (IOException e) { + e.printStackTrace(); + } + if(status == 0) + status = -1; + return status; + } + + public void close() { + try { + socket.close(); + } catch (IOException ignored) {} + } + +} From 00f4ef2e9cdcb86e563e553bd73ac6d8168060ff Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 18 Jan 2022 23:25:28 +0100 Subject: [PATCH 5/6] Added cookie parsing --- src/main/java/org/javawebstack/httpclient/HTTPRequest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/javawebstack/httpclient/HTTPRequest.java b/src/main/java/org/javawebstack/httpclient/HTTPRequest.java index 998f20d..b2c824c 100644 --- a/src/main/java/org/javawebstack/httpclient/HTTPRequest.java +++ b/src/main/java/org/javawebstack/httpclient/HTTPRequest.java @@ -230,6 +230,13 @@ public HTTPRequest execute() { requestImplementation.close(); } catch (IOException ignored) {} + for(String value : headers("set-cookie")) + responseCookies.addAll(HttpCookie.parse("set-cookie: "+value)); + for(String value : headers("set-cookie2")) + responseCookies.addAll(HttpCookie.parse("set-cookie2: "+value)); + if(client.isAutoCookies()) + cookies().forEach(client::cookie); + if (client.getAfterInterceptor() != null) client.getAfterInterceptor().intercept(this); return this; From 1054c09cef57700f66576a72bb20d6c28524b68f Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 18 Jan 2022 23:31:55 +0100 Subject: [PATCH 6/6] Hopefully fixed the readme maven badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f34fdf0..39f4fe9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ JWS HTTP Client ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/JavaWebStack/http-client/Maven%20Deploy/master) ![GitHub](https://img.shields.io/github/license/JavaWebStack/http-client) -![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Forg%2Fjavawebstack%2FHTTP-Client%2Fmaven-metadata.xml) +![Maven metadata URL](https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Forg%2Fjavawebstack%2Fhttp-client%2Fmaven-metadata.xml) ![GitHub contributors](https://img.shields.io/github/contributors/JavaWebStack/http-client) ![Lines of code](https://img.shields.io/tokei/lines/github/JavaWebStack/http-client) ![Discord](https://img.shields.io/discord/815612319378833408?color=%237289DA&label=discord)