HTTP
This module defines the ServiceTalk client and server API for the HTTP/1.x and HTTP/2 protocols. This module supports all the different Programming Paradigms for client and server. Here is a quick start example of the blocking and aggregated paradigm:
Blocking Client
try (BlockingHttpClient client = HttpClients.forSingleAddress("localhost", 8080).buildBlocking()) {
HttpResponse response = client.request(client.get("/sayHello"));
// use the response
}
Blocking Server
HttpServers.forPort(8080)
.listenBlockingAndAwait((ctx, request, responseFactory) ->
responseFactory.ok().payloadBody("Hello World!", textSerializer()))
.awaitShutdown();
Extensibility and Filters
The design of this protocol involves configuring builders for core protocol concerns, and then appending Filters
for
extensibility. Filters
are described in more detail below (e.g. Service Filters, Client Filters) but in
general they facilitate user code to filter/intercept/modify the request/response processing. Filters
can be used for
cross-cutting concerns such as authentication, authorization, logging, metrics, tracing, etc…
Server
The server side is built around the concept of Service
. A Service
is where your business logic lives. ServiceTalk’s
HTTP module will interact with a single service which is provided by the user via HttpServers
. The flow of data from
the socket to the HTTP Service
is visualized as follows:
+--------+ request +---------+ +---------+ | |--------->| HTTP |------>| HTTP | | Socket | | Decoder | | Service | | |<---------| Encoder |<------| | +--------+ response +---------+ +---------+
Each Service
has access to a
HttpServiceContext
which provides additional context
(via ConnectionContext)
into the Connection
/transport details for each request/response. This means that the HttpService
may be invoked
for multiple connections, from different threads, and even concurrently.
Service Filters
Filters provide a means to filter/intercept and modify each request/response life cycle. Service
Filters are used to
implement
tracing
metrics, logging,
basic auth,
and any other extension that needs request/response level visibility. The diagram below describes the control flow
as related to Service
filters:
+--------+ request +---------+ +---------+ +---------+ +---------+ | |--------->| HTTP |------>| HTTP |------>| HTTP |------>| HTTP | | Socket | | Decoder | | Service | | Service | | Service | | |<---------| Encoder |<------| Filter 1|<------| Filter n|<------| | +--------+ response +---------+ +---------+ +---------+ +---------+
To implement a Service
filter you should implement the
Service Filter Factory and append it
on the HttpServerBuilder
via
HttpServerBuilder#appendServiceFilter(..).
Currently we only support writing Filters for the
Asynchronous and Streaming
programming paradigm. An
Asynchronous and Streaming
filter can be used with a Service in any other programming paradigm.
|
Routers
In practice it is common for a HTTP Service
to handle many different types of request(s) that all have unique
processing requirements. The control flow in ServiceTalk is represented by a "Router". A "Router" is a Service
that
owns the responsibility of multiplexing the control flow. ServiceTalk does not mandate a specific "Router"
implementation but provides a couple reference implementations for common use cases (e.g.
Predicate Router and
JAX-RS via Jersey). The general component diagram of a "Router"
is as follows:
+---------+ /------>| Route 1 | | +---------+ +--------+ request +---------+ +---------+ | | |--------->| HTTP |------>| HTTP | | +---------+ | Socket | | Decoder | | Service |<------+------>| Route 2 | | |<---------| Encoder |<------| Router | | +---------+ +--------+ response +---------+ +---------+ | | +---------+ \------>| Route n | +---------+
Client
A Client
is generally responsible for managing multiple Connections
. There are a few flavors of HTTP Clients:
SingleAddress Client
This Client
will connect to a single unresolved address, that is provided while creating the client. The unresolved
address is resolved via an asynchronous DNS resolver by default (see Service Discovery). This Client
is for use
cases where you want to issue requests to a single service (that may have multiple instances).
MultiAddress Client
This Client
parses the request-target to determine the remote
address for each request. This Client
simulates a browser type of use case.
Each of the above Client
s can be created via the
HttpClients static factory.
The Client
manages multiple Connections
via a
LoadBalancer. The control flow of a request/response can be
visualized in the below diagram:
+--------------+ +----------------------+ +--------+ /--->| Connection 1 |<--->| HTTP Decoder/Encoder |<--->| Socket | | +--------------+ +----------------------+ +--------+ +--------+ request +--------------+ | | |--------->| | | +--------------+ +----------------------+ +--------+ | Client | | LoadBalancer |<---+--->| Connection 2 |<--->| HTTP Decoder/Encoder |<--->| Socket | | |<---------| | | +--------------+ +----------------------+ +--------+ +--------+ response +--------------+ | | +--------------+ +----------------------+ +--------+ \--->| Connection x |<--->| HTTP Decoder/Encoder |<--->| Socket | +--------------+ +----------------------+ +--------+
The LoadBalancer is consulted for each request to determine which connection should be used.
Client Filters
Filters provide a means to filter/intercept and modify each request/response life cycle. Client
Filters are used to
implement
tracing
metrics, logging, authorization, and any other extension that needs request/response level visibility.
+--------------+ +----------------------+ +--------+ /--->| Connection 1 |<--->| HTTP Decoder/Encoder |<--->| Socket | | +--------------+ +----------------------+ +--------+ +--------+ request +---------+ +---------+ +--------------+ | | |--------->| |---->| |---->| | | +--------------+ +----------------------+ +--------+ | Client | | Client | | Client | | LoadBalancer |<---+--->| Connection 2 |<--->| HTTP Decoder/Encoder |<--->| Socket | | |<---------| Filter 1|<----| Filter n|<----| | | +--------------+ +----------------------+ +--------+ +--------+ response +---------+ +---------+ +--------------+ | | +--------------+ +----------------------+ +--------+ \--->| Connection x |<--->| HTTP Decoder/Encoder |<--->| Socket | +--------------+ +----------------------+ +--------+
To implement a Client
filter you should implement the
Client Filter Factory and append it on
the HttpClientBuilder
via
HttpClientBuilder#appendClientFilter(..).
Currently we only support writing Filters for the
Asynchronous and Streaming
programming paradigm. An
Asynchronous and Streaming
filter can be used with a Client in any other programming paradigm.
|
Connection Filters
The Client
doesn’t have visibility into Connection
specific information. For example, the Connection
layer knows
about transport details such as connected remote address and other elements in the
ConnectionContext.
If you have use cases that require this information in the request/response control flow you can use a
Connection Filter
. The diagram below illustrates how the Connection Filter
interacts with the request/response
control flow.
+---------------------+ +---------------------+ +--------------+ +----------------------+ +--------+ /--->| Connection Filter 1 |<--->| Connection Filter n |<--->| Connection 1 |<--->| HTTP Decoder/Encoder |<--->| Socket | | +---------------------+ +---------------------+ +--------------+ +----------------------+ +--------+ +--------+ request +--------------+ | | |--------->| | | +---------------------+ +---------------------+ +--------------+ +----------------------+ +--------+ | Client | | LoadBalancer |<---+--->| Connection Filter 1 |<--->| Connection Filter n |<--->| Connection 2 |<--->| HTTP Decoder/Encoder |<--->| Socket | | |<---------| | | +---------------------+ +---------------------+ +--------------+ +----------------------+ +--------+ +--------+ response +--------------+ | | +---------------------+ +---------------------+ +--------------+ +----------------------+ +--------+ \--->| Connection Filter 1 |<--->| Connection Filter n |<--->| Connection x |<--->| HTTP Decoder/Encoder |<--->| Socket | +---------------------+ +---------------------+ +--------------+ +----------------------+ +--------+
Service Discovery
Another core component of the Client
is the
ServiceDiscoverer. The
ServiceDiscoverer
is responsible for resolving a service address into a set of resolved addresses used to create
Connection
(s) by the LoadBalancer
. The default implementation for HTTP is DNS and will resolve the IP addresses of
each service address every TTL seconds. ServiceDiscoverer
s
are typically not invoked in the request/response path and addresses are resolved "out of band", a.k.a in the
background.
+------------+ | Service | | Discoverer | +------------+ ^ | | +--------------+ | /--->| Connection 1 | V | +--------------+ +--------+ request +--------------+ | | |--------->| | | +--------------+ | Client | | LoadBalancer |<---+--->| Connection 2 | | |<---------| | | +--------------+ +--------+ response +--------------+ | | +--------------+ \--->| Connection x | +--------------+
Using HTTP/2 transport
HTTP/2 provides many benefits over HTTP/1.1 ranging from improving performance (eg: multiplexing, binary framing) to adding new features (eg: server push, request prioritization). ServiceTalk intends to provide all these features to users eventually, but for a majority of cases, performance benefits of HTTP/2 are more beneficial than the effort required to use the new features. As an interim measure, ServiceTalk provides an option for users to use HTTP/2 as the underlying transport for HTTP clients/servers while using the same API as HTTP/1.1. This makes it easy for users to leverage HTTP/2 performance benefits with minimal code change. We intend to make HTTP/2 specific features available in ServiceTalk eventually.
For the cleartext TCP connections, users have to configure the desired protocol version upfront, using builder methods. For secure TLS connections, a protocol version must be negotiated using ALPN extension.
For more information about how to configure different HTTP protocol versions see these examples.
Serialization
Serialization factories are made available in the HttpSerializationProviders static factory class.
The core abstractions
HttpDeserializer and
HttpSerializer are designed to be coupled to a
specific Java type T
and accessed via a
HttpSerializationProvider. The
HttpDeserializer and
HttpSerializer are also designed to handle the HTTP
headers data behind the scenes. This means either checking if content-type
format is compatible with the
deserialization format and also adding a content-type
header identifying the resulting serialization format.
For more information about how to use serializers and deserializers see these examples.