Akka HTTP model contains a deeply structured, fully immutable, case-class based model of all the major HTTP data structures, like HTTP requests, responses and common headers. It lives in theakka-http-core module and forms the basis for most of Akka HTTP’s APIs.
2.2.1 Overview
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a few places around the code base (and probably your own code as well):
import akka.http.javadsl.model.*;
import akka.http.javadsl.model.headers.*; This brings all of the most relevant types in scope, mainly:
• HttpRequestandHttpResponse, the central message model
• headers, the package containing all the predefined HTTP header models and supporting types • Supporting types likeUri,HttpMethods,MediaTypes,StatusCodes, etc.
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait), while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of the type plus a trailing plural ‘s’.
For example:
• DefinedHttpMethodinstances are defined as static fields of theHttpMethodsclass. • DefinedHttpCharsetinstances are defined as static fields of theHttpCharsetsclass. • DefinedHttpEncodinginstances are defined as static fields of theHttpEncodingsclass. • DefinedHttpProtocolinstances are defined as static fields of theHttpProtocolsclass. • DefinedMediaTypeinstances are defined as static fields of theMediaTypesclass.
• DefinedStatusCodeinstances are defined as static fields of theStatusCodesclass.
2.2.2 HttpRequest
HttpRequestandHttpResponseare the basic immutable classes representing HTTP messages. AnHttpRequestconsists of
• a method (GET, POST, etc.) • a URI
• a seq of headers • an entity (body data) • a protocol
Here are some examples how to construct anHttpRequest: // construct a simple GET request to `homeUri` Uri homeUri = Uri.create("/home");
HttpRequest request1 = HttpRequest.create().withUri(homeUri);
// construct simple GET request to "/index" using helper methods HttpRequest request2 = HttpRequest.GET("/index");
// construct simple POST request containing entity ByteString data = ByteString.fromString("abc");
HttpRequest postRequest1 = HttpRequest.POST("/receive").withEntity(data);
// customize every detail of HTTP request //import HttpProtocols._
//import MediaTypes._
Authorization authorization = Authorization.basic("user", "pass"); HttpRequest complexRequest =
HttpRequest.PUT("/user")
.withEntity(HttpEntities.create(MediaTypes.TEXT_PLAIN.toContentType(), "abc")) .addHeader(authorization)
.withProtocol(HttpProtocols.HTTP_1_0);
In its basic formHttpRequest.createcreates an empty default GET request without headers which can then be transformed using one of thewithXmethods,addHeader, oraddHeaders. Each of those will create a new immutable instance, so instances can be shared freely. There exist some overloads forHttpRequest.create
that simplify creating requests for common cases. Also, to aid readability, there are predefined alternatives for
createnamed after HTTP methods to create a request with a given method and uri directly.
2.2.3 HttpResponse
AnHttpResponseconsists of • a status code
• an entity (body data) • a protocol
Here are some examples how to construct anHttpResponse:
// simple OK response without data created using the integer status code HttpResponse ok = HttpResponse.create().withStatus(200);
// 404 response created using the named StatusCode constant
HttpResponse notFound = HttpResponse.create().withStatus(StatusCodes.NOT_FOUND);
// 404 response with a body explaining the error HttpResponse notFoundCustom =
HttpResponse.create() .withStatus(404)
.withEntity("Unfortunately, the resource couldn't be found.");
// A redirecting response containing an extra header
Location locationHeader = Location.create("http://example.com/other"); HttpResponse redirectResponse =
HttpResponse.create()
.withStatus(StatusCodes.FOUND) .addHeader(locationHeader);
In addition to the simpleHttpEntities.create methods which create an entity from a fixedStringor
ByteStringas shown here the Akka HTTP model defines a number of subclasses ofHttpEntitywhich allow body data to be specified as a stream of bytes. All of these types can be created using the method on
HttpEntites.
2.2.4 HttpEntity
AnHttpEntitycarries the data bytes of a message together with its Content-Type and, if known, its Content- Length. In Akka HTTP there are five different kinds of entities which model the various ways that message content can be received or sent:
HttpEntityStrict The simplest entity, which is used when all the entity are already available in memory. It wraps a plainByteStringand represents a standard, unchunked entity with a knownContent-Length. HttpEntityDefault The general, unchunked HTTP/1.1 message entity. It has a known length and presents its
data as a Source[ByteString] which can be only materialized once. It is an error if the provided source doesn’t produce exactly as many bytes as specified. The distinction ofHttpEntityStrictand
HttpEntityDefaultis an API-only one. One the wire, both kinds of entities look the same.
HttpEntityChunked The model for HTTP/1.1 chunked content (i.e. sent with Transfer-Encoding: chunked). The content length is unknown and the individual chunks are presented as a
Source[ChunkStreamPart]. AChunkStreamPart is either a non-empty chunk or the empty last chunk containing optional trailer headers. The stream consists of zero or more non-empty chunks parts and can be terminated by an optional last chunk.
HttpEntityCloseDelimited An unchunked entity of unknown length that is implicitly delimited by closing the connection (Connection: close). Content data is presented as aSource[ByteString]. Since the connection must be closed after sending an entity of this type it can only be used on the server-side for sending a response. Also, the main purpose ofCloseDelimitedentities is compatibility with HTTP/1.0 peers, which do not support chunked transfer encoding. If you are building a new application and are not constrained by legacy requirements you shouldn’t rely onCloseDelimitedentities, since implicit terminate-by-connection-close is not a robust way of signaling response end, especially in the presence of proxies. Additionally this type of entity prevents connection reuse which can seriously degrade perfor- mance. UseHttpEntityChunkedinstead!
HttpEntityIndefiniteLength A streaming entity of unspecified length for use in aMultipart.BodyPart.
Entity types HttpEntityStrict, HttpEntityDefault, and HttpEntityChunked are a sub- type of RequestEntity which allows to use them for requests and responses. In contrast,
HttpEntityCloseDelimitedcan only be used for responses.
Streaming entity types (i.e. all butHttpEntityStrict) cannot be shared or serialized. To create a strict, sharable copy of an entity or message useHttpEntity.toStrictor HttpMessage.toStrictwhich returns aFutureof the object with the body data collected into aByteString.
The classHttpEntitiescontains static methods to create entities from common types easily.
You can use theisX‘ methods of ‘‘HttpEntityto find out of which subclass an entity is if you want to provide special handling for each of the subtypes. However, in many cases a recipient of anHttpEntitydoesn’t care about of which subtype an entity is (and how data is transported exactly on the HTTP layer). Therefore, the general methodHttpEntity.getDataBytes()is provided which returns aSource<ByteString, ?>
that allows access to the data of an entity regardless of its concrete subtype. Note:
When to use which subtype?
• UseHttpEntityStrictif the amount of data is “small” and already available in memory (e.g. as aStringorByteString)
• UseHttpEntityDefaultif the data is generated by a streaming data source and the size of the data is known
• UseHttpEntityChunkedfor an entity of unknown length
• Use HttpEntityCloseDelimited for a response as a legacy alternative to
HttpEntityChunked if the client doesn’t support chunked transfer encoding. Otherwise useHttpEntityChunked!
• In aMultipart.Bodypart use HttpEntityIndefiniteLengthfor content of unknown length.
Caution: When you receive a non-strict message from a connection then additional data is only read from the network when you request it by consuming the entity data stream. This means that, if youdon’tconsume the entity stream then the connection will effectively be stalled. In particular, no subsequent message (request or response) will be read from the connection as the entity of the current message “blocks” the stream. There- fore you must make sure that you always consume the entity data, even in the case that you are not actually interested in it!
2.2.5 Header Model
Akka HTTP contains a rich model of the most common HTTP headers. Parsing and rendering is done automati- cally so that applications don’t need to care for the actual syntax of headers. Headers not modelled explicitly are represented as aRawHeader(which is essentially a String/String name/value pair).
See these examples of how to deal with headers: // create a ``Location`` header
Location locationHeader = Location.create("http://example.com/other");
// create an ``Authorization`` header with HTTP Basic authentication data Authorization authorization = Authorization.basic("user", "pass");
// a method that extracts basic HTTP credentials from a request
private Option<BasicHttpCredentials> getCredentialsOfRequest(HttpRequest request) { Option<Authorization> auth = request.getHeader(Authorization.class);
if (auth.isDefined() && auth.get().credentials() instanceof BasicHttpCredentials) return Option.some((BasicHttpCredentials) auth.get().credentials());
return Option.none(); }
2.2.6 HTTP Headers
When the Akka HTTP server receives an HTTP request it tries to parse all its headers into their respective model classes. Independently of whether this succeeds or not, the HTTP layer will always pass on all received headers to the application. Unknown headers as well as ones with invalid syntax (according to the header parser) will be made available asRawHeaderinstances. For the ones exhibiting parsing errors a warning message is logged depending on the value of theillegal-header-warningsconfig setting.
Some headers have special status in HTTP and are therefore treated differently from “regular” headers:
Content-Type The Content-Type of an HTTP message is modeled as the contentType field of the
HttpEntity. The Content-Typeheader therefore doesn’t appear in the headerssequence of a message. Also, aContent-Typeheader instance that is explicitly added to theheadersof a request or response will not be rendered onto the wire and trigger a warning being logged instead!
Transfer-Encoding Messages with Transfer-Encoding: chunked are represented as a
HttpEntityChunked entity. As such chunked messages that do not have another deeper nested transfer encoding will not have a Transfer-Encoding header in theirheaders list. Similarly, a
Transfer-Encodingheader instance that is explicitly added to theheadersof a request or response will not be rendered onto the wire and trigger a warning being logged instead!
Content-Length The content length of a message is modelled via itsHttpEntity. As such noContent-Length
header will ever be part of a message’sheadersequence. Similarly, aContent-Lengthheader instance that is explicitly added to theheadersof a request or response will not be rendered onto the wire and trigger a warning being logged instead!
Server AServerheader is usually added automatically to any response and its value can be configured via the
akka.http.server.server-headersetting. Additionally an application can override the config- ured header with a custom one by adding it to the response’sheadersequence.
User-Agent AUser-Agentheader is usually added automatically to any request and its value can be configured via theakka.http.client.user-agent-headersetting. Additionally an application can override the configured header with a custom one by adding it to the request’sheadersequence.
Date TheDateresponse header is added automatically but can be overridden by supplying it manually.
Connection On the server-side Akka HTTP watches for explicitly addedConnection: close response headers and as such honors the potential wish of the application to close the connection after the respective response has been sent out. The actual logic for determining whether to close the connection is quite involved. It takes into account the request’s method, protocol and potentialConnectionheader as well as the response’s protocol, entity and potentialConnectionheader. Seethis testfor a full table of what happens when.
2.2.7 Parsing / Rendering
Parsing and rendering of HTTP data structures is heavily optimized and for most types there’s currently no public API provided to parse (or render to) Strings or byte arrays.