As opposed to the Connection-level API the host-level API relieves you from manually opening and closing each individual HTTP connection. It autonomously manages a configurable pool of connections to one particular server.
Starting an HttpHostConnector
The core of this API is the HttpHostConnector actor, whose class, as with all other spray-can actors, you don’t get in direct contact with from your application. All communication happens purely via actor messages, the majority of which are defined in the spray.can.Http object.
You ask spray-can to start a new HttpHostConnector for a given host by sending an Http.HostConnectorSetup message to the Http extension as such:
IO(Http) ! Http.HostConnectorSetup("www.spray.io", port = 80)
Apart from the host name and port the Http.HostConnectorSetup message also allows you to specify socket options and a larger number of configuration settings for the connector and the connections it is to manage.
If there is no connector actor running for the given combination of hostname, port and settings spray-can will start a new one, otherwise the existing one is going to be re-used. The connector will then respond with an Http.HostConnectorInfo event message, which repeats the connectors ActorRef and setup command (for easy matching against the result of an “ask”).
Using an HttpHostConnector
Once you’ve got a hold of the connectors ActorRef you can send it one or more spray-http HttpRequestPart messages. The connector will send the request across one of the connections it manages according to the following logic:
- if HTTP pipelining is not enabled (the default) the request is
- dispatched to the first idle connection in the pool if there is one
- dispatched to a newly opened connection if there is no idle one and less than the configured max-connections have been opened so far
- queued and sent across the first connection that becomes available (i.e. either idle or unconnected) if all available connections are currently busy with open requests
- if HTTP pipelining is enabled the request is dispatched to
- the first idle connection in the pool if there is one
- a newly opened connection if there is no idle one and less than the configured max-connections have been opened so far
- the connection with the least open requests if all connections already have requests open
As soon as a response for a request has been received it is dispatched as a HttpResponsePart instance to the sender of the respective request. If the server indicated that it doesn’t want to reuse the connection for other requests (either via a Connection: close header on an HTTP/1.1 response or a missing Connection: Keep-Alive header on an HTTP/1.0 response) the connector actor closes the connection after receipt of the response thereby freeing up the “slot” for a new connection.
Retrying a Request
If the max-retries connector config setting is greater than zero the connector retries idempotent requests for which a response could not be successfully retrieved. Idempotent requests are those whose HTTP method is defined to be idempotent by the HTTP spec, which are all the ones currently modelled by spray-http except for the PATCH and POST methods.
When a response could not be received for a certain request there are essentially three possible error scenarios:
- The request got lost on the way to the server.
- The server experiences a problem while processing the request.
- The response from the server got lost on the way back.
Since the host connector cannot know which one of these possible reasons caused the problem and therefore PATCH and POST requests could have already triggered a non-idempotent action on the server these requests cannot be retried.
In these cases, as well as when all retries have not yielded a proper response, the connector dispatches a Status.Failure message with a RuntimeException holding a respective error message to the sender of the request.
The connector config contains an idle-timeout setting which specifies the time period after which an idle connector, i.e. one without any open connections, will automatically shut itself down. Since, by default, the connections in the connectors connection pool also have an idle-timeout active an unused connector will eventually be cleaned up completely if left unused.
However, in order to speed up the shutdown a host connector can be sent an Http.CloseAll command, which triggers an explicit closing of all connections. After all connections have been properly closed the connector will dispatch an Http.ClosedAll event message to all senders of Http.CloseAll messages before stopping itself.
A subsequent sending of an identical Http.HostConnectorSetup command to the Http extension will then trigger the creation of a fresh connector instance.