Skip to content
This repository has been archived by the owner on Jan 5, 2022. It is now read-only.

Commit

Permalink
v1.0.0 (#172)
Browse files Browse the repository at this point in the history
#157: Add support for OPTIONS and TRACE request methods
#149: Parsing of responses with empty body and no content-type
#155: Add optional shadow jar distributions
#162: Problem encoding / as %2F for path elements in URI
#169: UTF-8 encoding and missing Accept-Charset header
#163: Visibility of request details
  • Loading branch information
cjstehno committed Sep 22, 2017
1 parent 6b3b551 commit 946613d
Show file tree
Hide file tree
Showing 36 changed files with 1,769 additions and 286 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ build
conftarget/
*.ipr
*.iws
classes/
classes/
**/out/**
.gradletasknamecache
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ Hopefully that gives you a general idea of how Http Builder NG works. Http Build

Http Builder NG artifacts are available on [Bintray](https://bintray.com/http-builder-ng/dclark/http-builder-ng) and Maven Central, for Gradle you can add the following dependency to your `build.gradle` file `dependencies` closure:

compile 'io.github.http-builder-ng:http-builder-ng-CLIENT:0.18.0'
compile 'io.github.http-builder-ng:http-builder-ng-CLIENT:1.0.0'

or, for Maven add the following to your `pom.xml` file:

<dependency>
<groupId>io.github.http-builder-ng</groupId>
<artifactId>http-builder-ng-CLIENT</artifactId>
<version>0.18.0</version>
<version>1.0.0</version>
</dependency>

where `CLIENT` is replaced with the client library name (`core`, `apache`, or `okhttp`).
Expand Down
11 changes: 9 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ plugins {
id 'jacoco'
id 'org.asciidoctor.convert' version '1.5.3'
id 'com.stehno.gradle.site' version '0.0.3'
id "com.github.johnrengelman.shadow" version '2.0.1'
}

group = 'io.github.http-builder-ng'
version = '0.18.0'
version = '1.0.0'

repositories {
jcenter()
Expand All @@ -28,6 +29,7 @@ subprojects {
apply plugin: 'maven-publish'
apply plugin: 'jacoco'
apply plugin: 'findbugs'
apply plugin: 'com.github.johnrengelman.shadow'

group = project.parent.group
version = project.parent.version
Expand Down Expand Up @@ -61,7 +63,10 @@ subprojects {
}

testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'com.stehno.ersatz:ersatz:1.3.0'

testCompile 'com.stehno.ersatz:ersatz:1.5.0:safe@jar'
testCompile 'org.hamcrest:hamcrest-library:1.3'

testCompile 'com.stehno.vanilla:vanilla-core:0.5.1'

testCompile 'org.slf4j:jcl-over-slf4j:1.7.25'
Expand Down Expand Up @@ -118,6 +123,7 @@ subprojects {
archives jar
archives sourcesJar
archives javadocJar
archives shadowJar
}

publishing {
Expand All @@ -126,6 +132,7 @@ subprojects {
from components.java
artifact sourcesJar
artifact javadocJar
artifact shadowJar
groupId project.group
artifactId project.name
version project.version
Expand Down
24 changes: 24 additions & 0 deletions http-builder-ng-apache/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,27 @@ bintray {
}
}
}

shadowJar {
classifier = 'safe'

dependencies {
exclude(dependency('commons-logging:commons-logging:'))
exclude(dependency('com.fasterxml.jackson.core:jackson-databind:'))
exclude(dependency('com.opencsv:opencsv:'))
exclude(dependency('javax.mail:javax.mail-api:'))
exclude(dependency('com.sun.main:javax.mail:'))
exclude(dependency('net.sourceforge.nekohtml:nekohtml:'))
exclude(dependency('org.codehaus.groovy:groovy-all:'))
exclude(dependency('org.jsoup:jsoup:'))
exclude(dependency('oauth.signpost:signpost-commonshttp4:'))
exclude(dependency('oauth.signpost:signpost-core:'))
exclude(dependency('org.slf4j:slf4j-api:'))
}

relocate 'org.apache.http', 'httpb.http'
relocate 'mozilla', 'httpb.mozilla'
relocate 'org.apache.commons.codec', 'httpb.codec'
relocate 'org.apache.env', 'httpb.env'
relocate 'org.apache.xml.resolver', 'httpb.xml.resolver'
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Copyright (C) 2017 HttpBuilder-NG Project
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -19,13 +19,7 @@
import groovy.lang.DelegatesTo;
import groovyx.net.http.util.IoUtils;
import org.apache.http.*;
import org.apache.http.protocol.HttpContext;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.auth.AuthScope;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
Expand All @@ -34,28 +28,36 @@
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.io.EmptyInputStream;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.NoSuchAlgorithmException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Proxy;
import java.net.URI;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -69,7 +71,7 @@

/**
* `HttpBuilder` implementation based on the https://hc.apache.org/httpcomponents-client-ga/[Apache HttpClient library].
*
*
* Generally, this class should not be used directly, the preferred method of instantiation is via one of the two static `configure()` methods of this
* class or using one of the `configure` methods of `HttpBuilder` with a factory function for this builder.
*/
Expand All @@ -80,11 +82,11 @@ public class ApacheHttpBuilder extends HttpBuilder {

/**
* Creates an `HttpBuilder` using the `ApacheHttpBuilder` factory instance configured with the provided configuration closure.
*
*
* The configuration closure delegates to the {@link HttpObjectConfig} interface, which is an extension of the {@link HttpConfig} interface -
* configuration properties from either may be applied to the global client configuration here. See the documentation for those interfaces for
* configuration property details.
*
*
* [source,groovy]
* ----
* def http = HttpBuilder.configure {
Expand All @@ -101,13 +103,13 @@ public static HttpBuilder configure(@DelegatesTo(HttpObjectConfig.class) final C

/**
* Creates an `HttpBuilder` using the `ApacheHttpBuilder` factory instance configured with the provided configuration function.
*
*
* The configuration {@link Consumer} function accepts an instance of the {@link HttpObjectConfig} interface, which is an extension of the {@link HttpConfig}
* interface - configuration properties from either may be applied to the global client configuration here. See the documentation for those interfaces for
* configuration property details.
*
*
* This configuration method is generally meant for use with standard Java.
*
*
* [source,java]
* ----
* HttpBuilder.configure(new Consumer<HttpObjectConfig>() {
Expand All @@ -116,9 +118,9 @@ public static HttpBuilder configure(@DelegatesTo(HttpObjectConfig.class) final C
* }
* });
* ----
*
*
* Or, using lambda expressions:
*
*
* [source,java]
* ----
* HttpBuilder.configure(config -> {
Expand Down Expand Up @@ -163,34 +165,32 @@ public Socket createSocket(final HttpContext context) {
private SSLContext sslContext(final HttpObjectConfig config) {
try {
return (config.getExecution().getSslContext() != null ?
config.getExecution().getSslContext() :
SSLContext.getDefault());
}
catch(NoSuchAlgorithmException e) {
config.getExecution().getSslContext() :
SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

private Registry<ConnectionSocketFactory> registry(final HttpObjectConfig config) {
final ProxyInfo proxyInfo = config.getExecution().getProxyInfo();

final boolean isSocksProxied = (proxyInfo != null && proxyInfo.getProxy().type() == Proxy.Type.SOCKS);

if(isSocksProxied) {
if (isSocksProxied) {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new SocksHttp(proxyInfo.getProxy()))
.register("https", new SocksHttps(proxyInfo.getProxy(), sslContext(config),
config.getExecution().getHostnameVerifier()))
config.getExecution().getHostnameVerifier()))
.build();
}
else {
} else {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslContext(config), config.getExecution().getHostnameVerifier()))
.build();
}
}

private class ApacheFromServer implements FromServer {

private final HttpResponse response;
Expand Down Expand Up @@ -227,7 +227,7 @@ public InputStream getInputStream() {
}

public boolean getHasBody() {
return entity != null;
return entity != null && !(inputStream instanceof EmptyInputStream);
}

public int getStatusCode() {
Expand Down Expand Up @@ -344,18 +344,17 @@ public ApacheHttpBuilder(final HttpObjectConfig config) {
final HttpClientBuilder myBuilder = HttpClients.custom();

final Registry<ConnectionSocketFactory> registry = registry(config);

if (config.getExecution().getMaxThreads() > 1) {
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(config.getExecution().getMaxThreads());
cm.setDefaultMaxPerRoute(config.getExecution().getMaxThreads());
myBuilder.setConnectionManager(cm);
}
else {
} else {
final BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager(registry);
myBuilder.setConnectionManager(cm);
}

final SSLContext sslContext = config.getExecution().getSslContext();
if (sslContext != null) {
myBuilder.setSSLContext(sslContext);
Expand Down Expand Up @@ -440,27 +439,28 @@ private HttpClientContext context(final ChainedHttpConfig requestConfig) throws

return c;
}

private <T extends HttpRequestBase> Object exec(final ChainedHttpConfig requestConfig,
final Function<URI, T> constructor) {

private <T extends HttpRequestBase> Object exec(final ChainedHttpConfig requestConfig, final Function<URI, T> constructor) {
try {
final ChainedHttpConfig.ChainedRequest cr = requestConfig.getChainedRequest();
final URI theUri = cr.getUri().toURI();
final T request = constructor.apply(theUri);
addHeaders(cr, request);

if ((request instanceof HttpEntityEnclosingRequest) && cr.actualBody() != null) {
final HttpEntity entity = entity(requestConfig);
((HttpEntityEnclosingRequest) request).setEntity(entity);
request.setHeader(entity.getContentType());
}

if(proxyInfo != null && proxyInfo.getProxy().type() == Proxy.Type.HTTP) {
addHeaders(cr, request);

if (proxyInfo != null && proxyInfo.getProxy().type() == Proxy.Type.HTTP) {
HttpHost proxy = new HttpHost(proxyInfo.getAddress(), proxyInfo.getPort(), proxyInfo.isSecure() ? "https" : "http");
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
}

return client.execute(request, new Handler(requestConfig), context(requestConfig));

} catch (Exception e) {
return handleException(requestConfig.getChainedResponse(), e);
}
Expand All @@ -472,14 +472,20 @@ private HttpEntity entity(final ChainedHttpConfig config) {
return ats;
}

@SuppressWarnings("Duplicates")
private <T extends HttpUriRequest> void addHeaders(final ChainedHttpConfig.ChainedRequest cr, final T message) throws URISyntaxException {
for (Map.Entry<String, String> entry : cr.actualHeaders(new LinkedHashMap<>()).entrySet()) {
message.addHeader(entry.getKey(), entry.getValue());
}

final String contentType = cr.actualContentType();
if (contentType != null) {
message.addHeader("Content-Type", contentType);
final Charset charset = cr.actualCharset();
if (charset != null) {
message.setHeader("Content-Type", contentType + "; charset=" + charset.toString().toLowerCase());
} else {
message.setHeader("Content-Type", contentType);
}
}

for (Map.Entry<String, String> e : cookiesToAdd(clientConfig, cr).entrySet()) {
Expand Down Expand Up @@ -510,4 +516,14 @@ protected Object doPatch(final ChainedHttpConfig requestConfig) {
protected Object doDelete(final ChainedHttpConfig requestConfig) {
return exec(requestConfig, HttpDelete::new);
}

@Override
protected Object doOptions(final ChainedHttpConfig config) {
return exec(config, HttpOptions::new);
}

@Override
protected Object doTrace(final ChainedHttpConfig config) {
return exec(config, HttpTrace::new);
}
}
Loading

0 comments on commit 946613d

Please sign in to comment.