-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add bindings for Apache HTTP Client5
Add info about REST-assured to client5 signer readme Include info about new bindings in older client bindings README.md
- Loading branch information
1 parent
1b5a89a
commit 73896d1
Showing
10 changed files
with
484 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Apache HTTP Client 5 module - EdgeGrid Client for Java | ||
|
||
-[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.akamai.edgegrid/edgegrid-signer-apache-http-client5/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.akamai.edgegrid/edgegrid-signer-apache-http-client5) | ||
-[![Javadoc](http://www.javadoc.io/badge/com.akamai.edgegrid/edgegrid-signer-apache-http-client5.svg)](http://www.javadoc.io/doc/com.akamai.edgegrid/edgegrid-signer-apache-http-client5) | ||
|
||
This library | ||
implements [Akamai EdgeGrid Authentication](https://techdocs.akamai.com/developer/docs/authenticate-with-edgegrid) for | ||
Java. | ||
This particular module is a binding for the [Apache HTTP Client library version 5.x](https://hc.apache.org/). | ||
This project contains installation and usage instructions in the [README.md](../README.md). | ||
|
||
## Use Apache HTTP Client | ||
|
||
Include the following Maven dependency in your project POM: | ||
|
||
```xml | ||
<dependency> | ||
<groupId>com.akamai.edgegrid</groupId> | ||
<artifactId>edgegrid-signer-apache-http-client5</artifactId> | ||
<version>${version}</version> | ||
</dependency> | ||
``` | ||
|
||
Or in Gradle's `build.gradle.kts` | ||
```kotlin | ||
implementation("com.akamai.edgegrid:edgegrid-signer-apache-http-client5:$version") | ||
``` | ||
|
||
Create an HTTP client that will sign your HTTP request with a defined client credential: | ||
|
||
```java | ||
var client=HttpClientBuilder.create() | ||
.addRequestInterceptorFirst(new ApacheHttpClient5EdgeGridInterceptor(credential)) | ||
.setRoutePlanner(new ApacheHttpClient5EdgeGridRoutePlanner(credential)) | ||
.build(); | ||
|
||
var request=new HttpGet("http://endpoint.net/billing-usage/v1/reportSources"); | ||
client.execute(request,response->{ | ||
// response handler | ||
}); | ||
``` | ||
|
||
## Use with REST-assured | ||
|
||
[REST-assured](https://github.com/rest-assured/rest-assured) doesn't currently support Apache HTTP Client 5. Refer to | ||
this [README](/edgegrid-signer-apache-http-client/README.md) in `edgegrid-signer-apache-http-client` module to set up | ||
an interceptor for a legacy client. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
|
||
<parent> | ||
<artifactId>edgegrid-signer-parent</artifactId> | ||
<groupId>com.akamai.edgegrid</groupId> | ||
<version>5.0.0</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>edgegrid-signer-apache-http-client5</artifactId> | ||
<packaging>jar</packaging> | ||
<name>Apache HTTP Client 5 Library binding for EdgeGrid Client</name> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.akamai.edgegrid</groupId> | ||
<artifactId>edgegrid-signer-core</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>ch.qos.logback</groupId> | ||
<artifactId>logback-classic</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.github.tomakehurst</groupId> | ||
<artifactId>wiremock</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.httpcomponents.client5</groupId> | ||
<artifactId>httpclient5</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.httpcomponents.core5</groupId> | ||
<artifactId>httpcore5</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.hamcrest</groupId> | ||
<artifactId>hamcrest-all</artifactId> | ||
</dependency> | ||
<!-- Apache HTTP Client library uses JCL. --> | ||
<dependency> | ||
<groupId>org.slf4j</groupId> | ||
<artifactId>jcl-over-slf4j</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.slf4j</groupId> | ||
<artifactId>slf4j-api</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.testng</groupId> | ||
<artifactId>testng</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
52 changes: 52 additions & 0 deletions
52
...va/com/akamai/edgegrid/signer/apachehttpclient5/ApacheHttpClient5EdgeGridInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.akamai.edgegrid.signer.apachehttpclient5; | ||
|
||
import com.akamai.edgegrid.signer.ClientCredential; | ||
import com.akamai.edgegrid.signer.ClientCredentialProvider; | ||
import com.akamai.edgegrid.signer.Request; | ||
import com.akamai.edgegrid.signer.exceptions.RequestSigningException; | ||
import org.apache.hc.core5.http.EntityDetails; | ||
import org.apache.hc.core5.http.HttpRequest; | ||
import org.apache.hc.core5.http.HttpRequestInterceptor; | ||
import org.apache.hc.core5.http.protocol.HttpContext; | ||
|
||
/** | ||
* Apache HTTP Client5 Library interceptor that signs a request using EdgeGrid V1 signing algorithm. | ||
* Signing is a process of adding an Authorization header with a request signature. If signing fails then <code>RuntimeException</code> is thrown. | ||
*/ | ||
public class ApacheHttpClient5EdgeGridInterceptor implements HttpRequestInterceptor { | ||
|
||
private final ApacheHttpClient5EdgeGridRequestSigner binding; | ||
|
||
/** | ||
* Creates an EdgeGrid signing interceptor using the same {@link ClientCredential} for each | ||
* request. | ||
* | ||
* @param credential a {@link ClientCredential} | ||
*/ | ||
public ApacheHttpClient5EdgeGridInterceptor(ClientCredential credential) { | ||
this.binding = new ApacheHttpClient5EdgeGridRequestSigner(credential); | ||
} | ||
|
||
/** | ||
* Creates an EdgeGrid signing interceptor selecting a {@link ClientCredential} via | ||
* {@link ClientCredentialProvider#getClientCredential(Request)} for each request. | ||
* | ||
* @param clientCredentialProvider a {@link ClientCredentialProvider} | ||
*/ | ||
public ApacheHttpClient5EdgeGridInterceptor(ClientCredentialProvider clientCredentialProvider) { | ||
this.binding = new ApacheHttpClient5EdgeGridRequestSigner(clientCredentialProvider); | ||
} | ||
|
||
@Override | ||
public void process( | ||
HttpRequest request, | ||
EntityDetails entityDetails, | ||
HttpContext httpContext | ||
) { | ||
try { | ||
binding.sign(request, request); | ||
} catch (RequestSigningException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
.../com/akamai/edgegrid/signer/apachehttpclient5/ApacheHttpClient5EdgeGridRequestSigner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package com.akamai.edgegrid.signer.apachehttpclient5; | ||
|
||
import com.akamai.edgegrid.signer.AbstractEdgeGridRequestSigner; | ||
import com.akamai.edgegrid.signer.ClientCredential; | ||
import com.akamai.edgegrid.signer.ClientCredentialProvider; | ||
import com.akamai.edgegrid.signer.Request; | ||
import org.apache.hc.core5.http.Header; | ||
import org.apache.hc.core5.http.HttpEntityContainer; | ||
import org.apache.hc.core5.http.HttpRequest; | ||
import org.apache.hc.core5.http.io.entity.BufferedHttpEntity; | ||
import org.apache.hc.core5.http.io.entity.EntityUtils; | ||
|
||
import java.io.IOException; | ||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
|
||
/** | ||
* Apache HTTP Client5 binding for EdgeGrid signer for signing {@link HttpRequest}. | ||
*/ | ||
public class ApacheHttpClient5EdgeGridRequestSigner extends AbstractEdgeGridRequestSigner<HttpRequest, HttpRequest> { | ||
|
||
public ApacheHttpClient5EdgeGridRequestSigner(ClientCredential clientCredential) { | ||
super(clientCredential); | ||
} | ||
|
||
public ApacheHttpClient5EdgeGridRequestSigner(ClientCredentialProvider clientCredentialProvider) { | ||
super(clientCredentialProvider); | ||
} | ||
|
||
@Override | ||
protected URI requestUri(HttpRequest request) { | ||
return getUri(request); | ||
} | ||
|
||
@Override | ||
protected Request map(HttpRequest request) { | ||
Request.RequestBuilder builder = Request.builder() | ||
.method(request.getMethod()) | ||
.uri(getUri(request)) | ||
.body(serializeContent(request)); | ||
for (Header h : request.getHeaders()) { | ||
builder.header(h.getName(), h.getValue()); | ||
} | ||
|
||
return builder.build(); | ||
} | ||
|
||
private URI getUri(HttpRequest request) { | ||
try { | ||
return request.getUri(); | ||
} catch (URISyntaxException e) { | ||
throw new IllegalArgumentException(e.getMessage(), e); | ||
} | ||
} | ||
|
||
private byte[] serializeContent(HttpRequest request) { | ||
if (!(request instanceof HttpEntityContainer)) { | ||
return new byte[]{}; | ||
} | ||
|
||
var entityWithRequest = (HttpEntityContainer) request; | ||
var entity = entityWithRequest.getEntity(); | ||
if (entity == null) { | ||
return new byte[]{}; | ||
} | ||
|
||
try { | ||
// Buffer non-repeatable entities | ||
if (!entity.isRepeatable()) { | ||
entityWithRequest.setEntity(new BufferedHttpEntity(entity)); | ||
} | ||
return EntityUtils.toByteArray(entityWithRequest.getEntity()); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
@Override | ||
protected void setAuthorization(HttpRequest request, String signature) { | ||
request.setHeader("Authorization", signature); | ||
} | ||
|
||
@Override | ||
protected void setHost(HttpRequest request, String host, URI uri) { | ||
request.setHeader("Host", host); | ||
setRequestUri(request, uri); | ||
} | ||
|
||
private void setRequestUri(HttpRequest request, URI uri) { | ||
// temporary workaround for https://issues.apache.org/jira/browse/HTTPCORE-742 | ||
request.setPath(uri.getPath()); | ||
request.setUri(uri); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...a/com/akamai/edgegrid/signer/apachehttpclient5/ApacheHttpClient5EdgeGridRoutePlanner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.akamai.edgegrid.signer.apachehttpclient5; | ||
|
||
import com.akamai.edgegrid.signer.ClientCredential; | ||
import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner; | ||
import org.apache.hc.core5.http.HttpHost; | ||
import org.apache.hc.core5.http.protocol.HttpContext; | ||
|
||
import java.net.ProxySelector; | ||
|
||
public class ApacheHttpClient5EdgeGridRoutePlanner extends SystemDefaultRoutePlanner { | ||
|
||
private final ClientCredential clientCredential; | ||
|
||
public ApacheHttpClient5EdgeGridRoutePlanner(ClientCredential clientCredential) { | ||
super(ProxySelector.getDefault()); | ||
this.clientCredential = clientCredential; | ||
} | ||
|
||
@Override | ||
protected HttpHost determineProxy(HttpHost target, HttpContext context) { | ||
var hostname = clientCredential.getHost(); | ||
int port = -1; | ||
final int pos = hostname.lastIndexOf(":"); | ||
if (pos > 0) { | ||
try { | ||
port = Integer.parseInt(hostname.substring(pos + 1)); | ||
} catch (NumberFormatException ex) { | ||
throw new IllegalArgumentException("Host contains invalid port number: " + hostname); | ||
} | ||
hostname = hostname.substring(0, pos); | ||
} | ||
return new HttpHost("https", hostname, port); | ||
} | ||
} |
95 changes: 95 additions & 0 deletions
95
...dgegrid/signer/apachehttpclient5/ApacheHttpClient5EdgeGridInterceptorIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package com.akamai.edgegrid.signer.apachehttpclient5; | ||
|
||
import com.akamai.edgegrid.signer.ClientCredential; | ||
import com.github.tomakehurst.wiremock.WireMockServer; | ||
import com.github.tomakehurst.wiremock.matching.RequestPattern; | ||
import com.github.tomakehurst.wiremock.verification.LoggedRequest; | ||
import org.apache.hc.client5.http.classic.methods.HttpGet; | ||
import org.hamcrest.CoreMatchers; | ||
import org.hamcrest.MatcherAssert; | ||
import org.hamcrest.Matchers; | ||
import org.testng.annotations.AfterClass; | ||
import org.testng.annotations.BeforeClass; | ||
import org.testng.annotations.BeforeMethod; | ||
import org.testng.annotations.Test; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
|
||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; | ||
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; | ||
import static com.github.tomakehurst.wiremock.client.WireMock.get; | ||
import static com.github.tomakehurst.wiremock.client.WireMock.matching; | ||
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; | ||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; | ||
|
||
/** | ||
* Integration tests for {@link ApacheHttpClient5EdgeGridInterceptor}. | ||
*/ | ||
public class ApacheHttpClient5EdgeGridInterceptorIntegrationTest { | ||
|
||
static final String SERVICE_MOCK_HOST = "localhost"; | ||
|
||
WireMockServer wireMockServer = new WireMockServer(wireMockConfig().dynamicHttpsPort()); | ||
|
||
ClientCredential credential; | ||
|
||
private String getHost() { | ||
return SERVICE_MOCK_HOST + ":" + wireMockServer.httpsPort(); | ||
} | ||
|
||
@BeforeClass | ||
public void setUp() { | ||
wireMockServer.start(); | ||
credential = ClientCredential.builder() | ||
.accessToken("akaa-dm5g2bfwoodqnc6k-ju7vlao2wz6oz2rp") | ||
.clientToken("akaa-k7glklzuxkkh2ycw-oadjphopvpn6yjoj") | ||
.clientSecret("SOMESECRET") | ||
.host(getHost()) | ||
.build(); | ||
} | ||
|
||
@BeforeMethod | ||
public void reset() { | ||
wireMockServer.resetMappings(); | ||
wireMockServer.resetRequests(); | ||
} | ||
|
||
@AfterClass | ||
public void tearDownAll() { | ||
wireMockServer.stop(); | ||
} | ||
|
||
@Test | ||
public void testInterceptor() throws IOException { | ||
wireMockServer.stubFor(get(urlPathEqualTo("/billing-usage/v1/reportSources")) | ||
.withHeader("Authorization", matching(".*")) | ||
.withHeader("Host", equalTo(getHost())) | ||
.willReturn(aResponse() | ||
.withStatus(302) | ||
.withHeader("Location", "/billing-usage/v1/reportSources/alternative"))); | ||
|
||
wireMockServer.stubFor(get(urlPathEqualTo("/billing-usage/v1/reportSources/alternative")) | ||
.withHeader("Authorization", matching(".*")) | ||
.withHeader("Host", equalTo(getHost())) | ||
.willReturn(aResponse() | ||
.withStatus(200) | ||
.withHeader("Content-Type", "text/xml") | ||
.withBody("<response>Some content</response>"))); | ||
|
||
var request = new HttpGet("http://endpoint.net/billing-usage/v1/reportSources"); | ||
|
||
var client = HttpClientSetup.getHttpClientWithRelaxedSsl() | ||
.addRequestInterceptorFirst(new ApacheHttpClient5EdgeGridInterceptor(credential)) | ||
.setRoutePlanner(new ApacheHttpClient5EdgeGridRoutePlanner(credential)) | ||
.build(); | ||
|
||
client.execute(request, response -> null); | ||
|
||
List<LoggedRequest> loggedRequests = wireMockServer.findRequestsMatching(RequestPattern | ||
.everything()).getRequests(); | ||
|
||
MatcherAssert.assertThat(loggedRequests.get(0).getHeader("Authorization"), | ||
Matchers.not(CoreMatchers.equalTo(loggedRequests.get(1).getHeader("Authorization")))); | ||
} | ||
} |
Oops, something went wrong.