Jettyx is a lightweight HTTP client built on top of Jetty, designed to simplify HTTP requests and responses in modern Java applications. It supports multiple HTTP versions, integrates with popular serialization libraries like Jackson, and offers a clear and extensible API for interacting with external services.
- Multiple HTTP versions (HTTP/1.1, HTTP/2, and HTTP/3).
- Fluent API for defining HTTP requests easily.
- Synchronous and asynchronous request handling with
CompletableFuture
. - Support for custom serialization with
HttpMapper
. - Out of box easy authorization support
- Flexible request and response handling with direct body access or wrapped responses.
This project is divided into several modules, each providing different functionality:
-
jettyx-http2:
- Adds support for HTTP/2 communication using Jetty.
-
jettyx-http3:
- Adds support for HTTP/3 communication using Jetty.
-
jettyx-jackson:
- Integrates with Jackson for automatic JSON mapping to and from Java objects.
To include Jettyx in your project, add the following dependency:
implementation 'org.exploit:jettyx:0.1.3'
<dependency>
<groupId>org.exploit</groupId>
<artifactId>jettyx</artifactId>
<version>0.1.2</version>
</dependency>
First you should create a Jettyx instance. You can use single Jettyx instance for multiple api clients. It is Closeable
.
For instance, we will use Jackson for serialization and enable HTTP/2 support. To do this, first add modules to your build.gradle:
// Or jettyx-http3 for HTTP3
implementation 'org.exploit:jettyx-http2:0.1.2'
implementation 'org.exploit:jettyx-jackson:0.1.2'
Now call newBuilder to create a Jettyx instance and add relevant HTTP Mappers with adding supported HTTP versions. By default, Jettyx already handles HTTP/1.1 requests and http mapping for scalar types (String, Integer, etc).
Jettyx jettyx = Jettyx.newBuilder()
.addHttpMapper(JacksonHttpMapper.create()) // Using Jackson for serialization
.enableVersion(new Http2Version()) // Enable HTTP/2.
.build();
Then define interface with request and response types:
public interface ReqresApi {
@HttpRequest(path = "/api/users")
ListUsers listUsers(@Query("page") int page);
@HttpRequest(path = "/api/users")
CompletableFuture<ListUsers> listUsersFuture(@QueryMap Map<String, Object> map);
@HttpRequest(method = HttpMethod.POST, path = "/api/users")
HttpResponse<Created> createUser(@Body Create user);
@HttpRequest(method = HttpMethod.GET, path = "/api/users/{id}")
CompletableFuture<HttpResponse<SingleUser>> getUser(@Path("id") int id);
}
You can directly specify response type or wrap in HttpResponse
to access raw response, status code, etc. Using CompletableFuture
allows to execute asynchronous requests.
Now create an api client:
ReqresApi reqres = jettyx.newApiClient(new ConstantUrlProvider("https://reqres.in/"), new NoAuth())
.create(ReqresApi.class);
You can provide your own UrlProvider
and Authorization
implementations. If you don't need this, just specify url:
ReqresApi reqres = jettyx.newApiClient("https:://reqres.in/")
.create(ReqresApi.class);
To enable rate limiting, Jettyx supports Bucket4J out of box. For this, simply create Bucket instance and pass it to the api client creator:
var limit = Bandwidth.builder()
.capacity(10)
.refillGreedy(10, Duration.ofSeconds(1L))
.initialTokens(10)
.build();
var bucket = Bucket.builder()
.addLimit(limit)
.build();
ReqresApi reqres = jettyx.newApiClient("https://reqres.in/", new NoAuth(), bucket)
.create(ReqresApi.class);
Every called request waits and consumes 1 token BEFORE making the request in a blocking manner.
- Basic Authentication (
BasicAuth
): Encodes username and password in the Authorization header using Base64. - Bearer Authentication (
BearerAuth
): Adds a token in the Authorization header using the Bearer scheme. - Header-based Authentication (
HeaderAuth
): Custom header name and value. - Query Parameter Authentication (
QueryAuth
): Adds authorization data as URL query parameters. - No Authentication (
NoAuth
): Applies no authentication.
The following annotations can be used in your API interface to define HTTP request properties:
@Auth
- Applies a specific authorization method to the request.
@HttpRequest(method = HttpMethod.GET, path = "/sample")
MyResponse getSample(@Auth Authorization auth);
@Body
- Specifies the request body.@ContentType
- Sets the Content-Type header.
@ContentType("application/json")
@HttpRequest(method = HttpMethod.POST, path = "/sample")
MyResponse pushSample(@Body MyPojo pojo);
@Header
- Adds a single header to the request.
@HttpRequest(method = HttpMethod.GET, path = "/sample")
MyResponse getSample(@Header("X-Header") String header);
@HeaderMap
- Adds multiple headers to the request.
@HttpRequest(method = HttpMethod.GET, path = "/sample")
MyResponse getSample(@HeaderMap Map<String, String> headers);
@Path
- Specifies a path parameter.
@HttpRequest(method = HttpMethod.GET, path = "/sample/{id}")
MyResponse getSample(@Path("id") int id);
@Query
- Adds a query parameter.
@HttpRequest(method = HttpMethod.GET, path = "/sample")
MyResponse getSample(@Query("page") int page);
@QueryMap
- Adds multiple query parameters from a map. Note, all values will be just converted to string usingtoString()
.
@HttpRequest(method = HttpMethod.GET, path = "/sample")
MyResponse getSample(@QueryMap Map<String, Object> map);
This project is licensed under BSD 2-Clause License.