Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ final RequestOptions options = RequestOptions.builder()
.header("X-Custom-Header", "value");
```

### Custom HTTP Transport

By default, the client uses `DefaultHttpAdapter` to make HTTP requests. If you need to route
requests through a proxy, use a specific HTTP library, or add observability, you can supply your
own implementation of the `HttpAdapter` interface via `ClientOptions`. See the
[HttpAdapter implementation guide](docs/http-adapter-implementation-guide.md) for details, which
includes a full example implementation using OkHttp (the library's original default transport).

## Support

Looking for help? Please contact [support@recurly.com](mailto:support@recurly.com) or visit
Expand Down
71 changes: 71 additions & 0 deletions docs/examples/JdkHttpClientAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Example HttpAdapter implementation backed by java.net.http.HttpClient (JDK 11+).
//
// This file is documentation only — it is not compiled as part of this library, which
// targets Java 8. Copy it into your own project if you're on Java 11+ and want to use
// the JDK's built-in HTTP client instead of adding a third-party dependency.

import com.recurly.v3.http.HttpAdapter;
import com.recurly.v3.http.HttpResponse;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

public class JdkHttpClientAdapter implements HttpAdapter {

private final HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(60))
.build();

@Override
public HttpResponse execute(String method, String url,
Map<String, String> headers, String body) throws IOException {
HttpRequest.Builder builder = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(60));

for (Map.Entry<String, String> header : headers.entrySet()) {
builder.header(header.getKey(), header.getValue());
}

HttpRequest.BodyPublisher bodyPublisher = body != null
? HttpRequest.BodyPublishers.ofString(body)
: HttpRequest.BodyPublishers.noBody();

switch (method) {
case "HEAD": builder.method("HEAD", HttpRequest.BodyPublishers.noBody()); break;
case "GET": builder.GET(); break;
case "POST": builder.POST(bodyPublisher); break;
case "PUT": builder.PUT(bodyPublisher); break;
case "DELETE": builder.DELETE(); break;
default:
throw new IllegalArgumentException(method + " is not a valid Recurly HTTP method");
}

java.net.http.HttpResponse<byte[]> response;
try {
response = client.send(builder.build(), BodyHandlers.ofByteArray());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Request interrupted", e);
}

int statusCode = response.statusCode();

Map<String, String> responseHeaders = new HashMap<>();
response.headers().map().forEach((name, values) -> {
if (!values.isEmpty()) {
responseHeaders.put(name, values.get(0));
}
});

byte[] responseBodyBytes = "HEAD".equals(method) ? new byte[0] : response.body();

return new HttpResponse(statusCode, responseHeaders, responseBodyBytes);
}
}
69 changes: 69 additions & 0 deletions docs/examples/OkHttpAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Example HttpAdapter implementation backed by OkHttp.
//
// This file is documentation only — it is not compiled as part of this library
// and OkHttp is NOT a dependency of this project. Copy it into your own
// project and add the OkHttp dependency shown in
// docs/http-adapter-implementation-guide.md if you want to use it.

import com.recurly.v3.http.HttpAdapter;
import com.recurly.v3.http.HttpResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class OkHttpAdapter implements HttpAdapter {

private final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();

@Override
public HttpResponse execute(String method, String url,
Map<String, String> headers, String body) throws IOException {
Request.Builder builder = new Request.Builder().url(url);

for (Map.Entry<String, String> header : headers.entrySet()) {
builder.header(header.getKey(), header.getValue());
}

RequestBody requestBody = body != null
? RequestBody.create(body, MediaType.parse("application/json; charset=utf-8"))
: RequestBody.create(new byte[0], MediaType.parse("application/json; charset=utf-8"));

switch (method) {
case "HEAD": builder.head(); break;
case "GET": builder.get(); break;
case "POST": builder.post(requestBody); break;
case "PUT": builder.put(requestBody); break;
case "DELETE": builder.delete(); break;
default:
throw new IllegalArgumentException(method + " is not a valid Recurly HTTP method");
}

try (Response response = client.newCall(builder.build()).execute()) {
int statusCode = response.code();

Map<String, String> responseHeaders = new HashMap<>();
Headers okHeaders = response.headers();
for (int i = 0; i < okHeaders.size(); i++) {
responseHeaders.put(okHeaders.name(i).toLowerCase(), okHeaders.value(i));
}

ResponseBody responseBody = response.body();
byte[] responseBodyBytes = responseBody != null ? responseBody.bytes() : new byte[0];

return new HttpResponse(statusCode, responseHeaders, responseBodyBytes);
}
}
}
Loading