Skip to content

Commit

Permalink
add http tool
Browse files Browse the repository at this point in the history
  • Loading branch information
wooEnrico committed Jun 14, 2024
1 parent 46de08b commit 08a9914
Show file tree
Hide file tree
Showing 14 changed files with 635 additions and 1 deletion.
11 changes: 10 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,16 @@
<artifactId>version</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.github.wooenrico</groupId>
<artifactId>tools</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.github.wooenrico</groupId>
<artifactId>utils</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
Expand Down
4 changes: 4 additions & 0 deletions tools/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<groupId>io.github.wooenrico</groupId>
<artifactId>version</artifactId>
</dependency>
<dependency>
<groupId>io.github.wooenrico</groupId>
<artifactId>utils</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package io.github.wooenrico.http.URLConnection;


import io.github.wooenrico.InputStreamUtil;
import io.github.wooenrico.http.common.HttpExecutor;
import io.github.wooenrico.http.common.HttpRequest;
import io.github.wooenrico.http.common.HttpResponse;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

public class NativeHttpClient implements HttpExecutor {

private final SSLContext sslContext;
private final HostnameVerifier hostnameVerifier;
private final Proxy proxy;
private final int connectTimeout;
private final int readTimeout;

public NativeHttpClient(SSLContext sslContext, HostnameVerifier hostnameVerifier, Proxy proxy, int connectTimeout, int readTimeout) {
this.sslContext = sslContext;
this.hostnameVerifier = hostnameVerifier;
this.proxy = proxy;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
}

public SSLContext getSslContext() {
return sslContext;
}

public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
}

public Proxy getProxy() {
return proxy;
}

public int getConnectTimeout() {
return connectTimeout;
}

public int getReadTimeout() {
return readTimeout;
}

public HttpURLConnection getUrlConnection(URL url) throws IOException {
URLConnection connection =
this.proxy == null ? url.openConnection() : url.openConnection(this.proxy);

if (connection instanceof HttpsURLConnection) {
if (this.sslContext != null) {
((HttpsURLConnection) connection)
.setSSLSocketFactory(this.sslContext.getSocketFactory());
}
if (this.hostnameVerifier != null) {
((HttpsURLConnection) connection).setHostnameVerifier(this.hostnameVerifier);
}
}

return (HttpURLConnection) connection;
}

@Override
public HttpResponse execute(HttpRequest httpRequest) throws IOException {
return doRequest(new URL(httpRequest.getUrl()), httpRequest.getMethod().name(), httpRequest.getHeaders(), httpRequest.getBody());
}

@Override
public CompletableFuture<HttpResponse> execute(HttpRequest httpRequest, Executor executor) {
CompletableFuture<HttpResponse> future = new CompletableFuture<>();
try {
if (executor == null) {
executor = Runnable::run;
}

executor.execute(() -> {
try {
HttpResponse httpResponse = execute(httpRequest);
future.complete(httpResponse);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
} catch (Exception e) {
future.completeExceptionally(e);
}
return future;
}

private HttpResponse doRequest(URL url, String requestMethod, Map<String, String> headers, byte[] body) throws IOException {

HttpURLConnection connection = getUrlConnection(url);
connection.setConnectTimeout(this.connectTimeout);
connection.setReadTimeout(this.readTimeout);
connection.setDoInput(true);
connection.setRequestMethod(requestMethod);

if (headers != null) {
headers.forEach(connection::setRequestProperty);
}

if (body != null && body.length > 0) {
connection.setDoOutput(true);
connection.setFixedLengthStreamingMode(body.length);

OutputStream outputStream = connection.getOutputStream();
outputStream.write(body);
outputStream.flush();
}

int responseCode = connection.getResponseCode();
String desc = "";
String version = "";

// HTTP/1.1 404 Not Found
// HTTP/1.0 200
String statusLine = connection.getHeaderField(0);

if (statusLine.startsWith("HTTP/1.")) {

int codeIndex = statusLine.indexOf(' ');

version = statusLine.substring(0, codeIndex);

if (codeIndex > 0) {
int phraseIndex = statusLine.indexOf(' ', codeIndex + 1);
if (phraseIndex > 0) {
desc = statusLine.substring(phraseIndex + 1);
}

if (phraseIndex < 0)
phraseIndex = statusLine.length();
try {
responseCode =
Integer.parseInt(statusLine.substring(codeIndex + 1, phraseIndex));
} catch (NumberFormatException ignore) {
}
}
}

Map<String, List<String>> headerFields = connection.getHeaderFields();
Map<String, List<String>> headerMap = new HashMap<>();
for (Map.Entry<String, List<String>> stringListEntry : headerFields.entrySet()) {
String key = stringListEntry.getKey();
if (key != null) {
headerMap.put(key, stringListEntry.getValue());
}
}

InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException e) {
inputStream = connection.getErrorStream();
}

byte[] bytes = InputStreamUtil.readAndClose(inputStream);

return new HttpResponse(responseCode, desc, version, headerMap, bytes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.github.wooenrico.http.URLConnection;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.net.Proxy;

public class NativeHttpClientBuilder {
private SSLContext sslContext;
private HostnameVerifier hostnameVerifier;
private Proxy proxy;
private int connectTimeout = 5000;
private int readTimeout = 10000;

public NativeHttpClientBuilder setSslContext(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}

public NativeHttpClientBuilder setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
return this;
}

public NativeHttpClientBuilder setProxy(Proxy proxy) {
this.proxy = proxy;
return this;
}

public NativeHttpClientBuilder setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}

public NativeHttpClientBuilder setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
return this;
}

public SSLContext getSslContext() {
return sslContext;
}

public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
}

public Proxy getProxy() {
return proxy;
}

public int getConnectTimeout() {
return connectTimeout;
}

public int getReadTimeout() {
return readTimeout;
}

public NativeHttpClient build() {
return new NativeHttpClient(this.sslContext, this.hostnameVerifier, this.proxy, this.connectTimeout, this.readTimeout);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.wooenrico.http.URLConnection;

import io.github.wooenrico.http.common.HttpProxy;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;

public class ProxyBasicAuthNativeHttpClient extends NativeHttpClient {

public ProxyBasicAuthNativeHttpClient(SSLContext sslContext, HostnameVerifier hostnameVerifier, Proxy proxy, int connectTimeout, int readTimeout) {
super(sslContext, hostnameVerifier, proxy, connectTimeout, readTimeout);
}

@Override
public HttpURLConnection getUrlConnection(URL url) throws IOException {
HttpURLConnection urlConnection = super.getUrlConnection(url);

if (getProxy() == null) {
return urlConnection;
}

if (getProxy() instanceof HttpProxy) {
HttpProxy httpProxy = (HttpProxy) getProxy();
urlConnection.setRequestProperty(HttpProxy.Proxy_Authorization_Header_Name,
httpProxy.getBasicAuthorization());
}

return urlConnection;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.wooenrico.http.URLConnection;

public class ProxyBasicAuthNativeHttpClientBuilder extends NativeHttpClientBuilder {
@Override
public ProxyBasicAuthNativeHttpClient build() {
return new ProxyBasicAuthNativeHttpClient(super.getSslContext(), super.getHostnameVerifier(), super.getProxy(), super.getConnectTimeout(), super.getReadTimeout());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.wooenrico.http.common;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

public interface HttpExecutor {
/**
* 执行http 请求
*
* @param httpRequest http请求内容
* @return HttpResponse
*/
HttpResponse execute(HttpRequest httpRequest) throws Exception;

/**
* 异步执行http请求
*
* @param httpRequest http请求内容
* @param executor 线程池
* @return CompletableFuture<HttpResponse>
*/
CompletableFuture<HttpResponse> execute(HttpRequest httpRequest, Executor executor);
}
51 changes: 51 additions & 0 deletions tools/src/main/java/io/github/wooenrico/http/common/HttpProxy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.github.wooenrico.http.common;

import java.net.Proxy;
import java.net.SocketAddress;
import java.util.Base64;

public class HttpProxy extends Proxy {

public final static String Proxy_Authorization_Header_Name = "Proxy-Authorization";

private String username;
private String password;

/**
* Creates an entry representing a PROXY connection.
* Certain combinations are illegal. For instance, for types Http, and
* Socks, a SocketAddress <b>must</b> be provided.
* <p>
* Use the {@code Proxy.NO_PROXY} constant
* for representing a direct connection.
*
* @param type the {@code Type} of the proxy
* @param sa the {@code SocketAddress} for that proxy
* @throws IllegalArgumentException when the type and the address are
* incompatible
*/
public HttpProxy(Type type, SocketAddress sa) {
super(type, sa);
}

public HttpProxy(Type type, SocketAddress sa, String username, String password) {
super(type, sa);
this.username = username;
this.password = password;
}

/**
* proxy auth header
*
* @return header value of Proxy-Authorization
*/
public String getBasicAuthorization() {
if (username == null && password == null) {
return null;
}

String encodeKey = username + ":" + password;
byte[] encode = Base64.getEncoder().encode(encodeKey.getBytes());
return "Basic" + " " + new String(encode);
}
}
Loading

0 comments on commit 08a9914

Please sign in to comment.