//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

= Server I/O Architecture

The Jetty server libraries provide the basic components and APIs to implement a network server.

They build on the common xref:arch/io.adoc[Jetty I/O Architecture] and provide server specific concepts.

The Jetty server libraries provide I/O support for TCP/IP sockets (for both IPv4 and IPv6) and, when using Java 16 or later, for Unix-Domain sockets.

Support for Unix-Domain sockets is interesting when Jetty is deployed behind a proxy or a load-balancer: it is possible to configure the proxy or load balancer to communicate with Jetty via Unix-Domain sockets, rather than via the loopback network interface.

The central I/O server-side component are `org.eclipse.jetty.server.ServerConnector`, that handles the TCP/IP socket traffic, and `org.eclipse.jetty.unixdomain.server.UnixDomainServerConnector`, that handles the Unix-Domain socket traffic.

`ServerConnector` and `UnixDomainServerConnector` are very similar, and while in the following sections `ServerConnector` is used, the same concepts apply to `UnixDomainServerConnector`, unless otherwise noted.

A `ServerConnector` manages a list of ``ConnectionFactory``s, that indicate what protocols the connector is able to speak.

[[connection-factory]]
== Creating Connections with `ConnectionFactory`

Recall from the xref:arch/io.adoc#connection[`Connection` section] of the Jetty I/O architecture that `Connection` instances are responsible for parsing bytes read from a socket and generating bytes to write to that socket.

On the server-side, a `ConnectionFactory` creates `Connection` instances that know how to parse and generate bytes for the specific protocol they support -- it can be either HTTP/1.1, or TLS, or FastCGI, or the https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt[PROXY protocol].

For example, this is how clear-text HTTP/1.1 is configured for TCP/IP sockets:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=http]
----

With this configuration, the `ServerConnector` will listen on port `8080`.

Similarly, this is how clear-text HTTP/1.1 is configured for Unix-Domain sockets:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=httpUnix]
----

With this configuration, the `UnixDomainServerConnector` will listen on file `/tmp/jetty.sock`.

[NOTE]
====
`ServerConnector` and `UnixDomainServerConnector` only differ by how they are configured -- for `ServerConnector` you specify the IP port it listens to, for `UnixDomainServerConnector` you specify the Unix-Domain path it listens to.

Both configure ``ConnectionFactory``s in exactly the same way.
====

When a new socket connection is established, `ServerConnector` delegates to the `ConnectionFactory` the creation of the `Connection` instance for that socket connection, that is linked to the corresponding `EndPoint`:

[plantuml]
----
skinparam backgroundColor transparent
skinparam monochrome true
skinparam shadowing false
skinparam padding 5

hide members
hide circle

scale 1.5

circle network
circle application

network - SocketChannelEndPoint
SocketChannelEndPoint - HttpConnection
HttpConnection - application
----

For every socket connection there will be an `EndPoint` + `Connection` pair.

[[connection-factory-wrapping]]
== Wrapping a `ConnectionFactory`

A `ConnectionFactory` may wrap another `ConnectionFactory`; for example, the TLS protocol provides encryption for any other protocol.
Therefore, to support encrypted HTTP/1.1 (also known as `https`), you need to configure the `ServerConnector` with two ``ConnectionFactory``s -- one for the TLS protocol and one for the HTTP/1.1 protocol, like in the example below:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=tlsHttp]
----

With this configuration, the `ServerConnector` will listen on port `8443`.
When a new socket connection is established, the first `ConnectionFactory` configured in `ServerConnector` is invoked to create a `Connection`.
In the example above, `SslConnectionFactory` creates a `SslConnection` and then asks to its wrapped `ConnectionFactory` (in the example, `HttpConnectionFactory`) to create the wrapped `Connection` (an `HttpConnection`) and will then link the two ``Connection``s together, in this way:

[plantuml]
----
skinparam backgroundColor transparent
skinparam monochrome true
skinparam shadowing false
skinparam padding 5

hide members
hide circle

scale 1.5

circle network
circle application

network - SocketChannelEndPoint
SocketChannelEndPoint - SslConnection
SslConnection -- SslEndPoint
SslEndPoint - HttpConnection
HttpConnection - application
----

Bytes read by the `SocketChannelEndPoint` will be interpreted as TLS bytes by the `SslConnection`, then decrypted and made available to the `SslEndPoint` (a component part of `SslConnection`), which will then provide them to `HttpConnection`.

The application writes bytes through the `HttpConnection` to the `SslEndPoint`, which will encrypt them through the `SslConnection` and write the encrypted bytes to the `SocketChannelEndPoint`.

[[connection-factory-detecting]]
== Choosing `ConnectionFactory` via Bytes Detection

Typically, a network port is associated with a specific protocol.
For example, port `80` is associated with clear-text HTTP, while port `443` is associated with encrypted HTTP (that is, the TLS protocol wrapping the HTTP protocol, also known as `https`).

In certain cases, applications need to listen to the same port for two or more protocols, or for different but incompatible versions of the same protocol, which can only be distinguished by reading the initial bytes and figuring out to what protocol they belong to.

The Jetty server libraries support this case by placing a `DetectorConnectionFactory` in front of other ``ConnectionFactory``s.
`DetectorConnectionFactory` accepts a list of ``ConnectionFactory``s that implement `ConnectionFactory.Detecting`, which will be called to see if one of them recognizes the initial bytes.

In the example below you can see how to support both clear-text and encrypted HTTP/1.1 (i.e. both `http` and `https`) _on the same network port_:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=detector]
----

<1> Creates the `DetectorConnectionFactory` with the `SslConnectionFactory` as the only detecting `ConnectionFactory`.
With this configuration, the detector will delegate to `SslConnectionFactory` to recognize the initial bytes, which will detect whether the bytes are TLS protocol bytes.
<2> Creates the `ServerConnector` with `DetectorConnectionFactory` as the first `ConnectionFactory`, and `HttpConnectionFactory` as the next `ConnectionFactory` to invoke if the detection fails.

In the example above `ServerConnector` will listen on port 8181.
When a new socket connection is established, `DetectorConnectionFactory` is invoked to create a `Connection`, because it is the first `ConnectionFactory` specified in the `ServerConnector` list.
`DetectorConnectionFactory` reads the initial bytes and asks to its detecting ``ConnectionFactory``s if they recognize the bytes.
In the example above, the detecting ``ConnectionFactory`` is `SslConnectionFactory` which will therefore detect whether the initial bytes are TLS bytes.
If one of the detecting ``ConnectionFactory``s recognizes the bytes, it creates a `Connection`; otherwise `DetectorConnectionFactory` will try the next `ConnectionFactory` after itself in the `ServerConnector` list.
In the example above, the next `ConnectionFactory` after `DetectorConnectionFactory` is `HttpConnectionFactory`.

The final result is that when new socket connection is established, the initial bytes are examined: if they are TLS bytes, a `SslConnectionFactory` will create a `SslConnection` that wraps an `HttpConnection` as explained <<connection-factory-wrapping,here>>, therefore supporting `https`; otherwise they are not TLS bytes and an `HttpConnection` is created, therefore supporting `http`.

[[connection-factory-custom]]
== Writing a Custom `ConnectionFactory`

This section explains how to use the Jetty server-side libraries to write a generic network server able to parse and generate any protocol..

Let's suppose that we want to write a custom protocol that is based on JSON but has the same semantic as HTTP; let's call this custom protocol `JSONHTTP`, so that a request would look like this:

[,json]
----
{
  "type": "request",
  "method": "GET",
  "version": "HTTP/1.1",
  "uri": "http://localhost/path",
  "fields": {
    "content-type": "text/plain;charset=ASCII"
  },
  "content": "HELLO"
}
----

In order to implement this custom protocol, we need to:

* implement a `JSONHTTPConnectionFactory`
* implement a `JSONHTTPConnection`
* parse bytes and generate bytes in the `JSONHTTP` format
* design an easy to use API that applications use to process requests and respond

First, the `JSONHTTPConnectionFactory`:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=jsonHttpConnectionFactory]
----

Note how `JSONHTTPConnectionFactory` extends `AbstractConnectionFactory` to inherit facilities common to all `ConnectionFactory` implementations.

Second, the `JSONHTTPConnection`.
Recall from the xref:arch/io.adoc#echo[echo `Connection` example] that you need to override `onOpen()` to call `fillInterested()` so that the Jetty I/O system will notify your `Connection` implementation when there are bytes to read by calling `onFillable()`.
Furthermore, because the Jetty libraries are non-blocking and asynchronous, you need to use `IteratingCallback` to implement `onFillable()`:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=jsonHttpConnection]
----

Again, note how `JSONHTTPConnection` extends `AbstractConnection` to inherit facilities that you would otherwise need to re-implement from scratch.

When `JSONHTTPConnection` receives a full JSON object it calls `invokeApplication(...)` to allow the application to process the incoming request and produce a response.

At this point you need to design a non-blocking asynchronous API that takes a `Callback` parameter so that applications can signal to the implementation when the request processing is complete (either successfully or with a failure).

A simple example of this API design could be the following:

* Wrap the JSON `Map` into a `JSONHTTPRequest` parameter so that applications may use more specific HTTP APIs such as `JSONHTTPRequest.getMethod()` rather than a generic `Map.get("method")`
* Provide an equivalent `JSONHTTPResponse` parameter so that applications may use more specific APIs such as `JSONHTTPResponse.setStatus(int)` rather than a generic `Map.put("status", 200)`
* Provide a `Callback` (or a `CompletableFuture`) parameter so that applications may indicate when the request processing is complete

This results in the following API:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ServerDocs.java[tags=jsonHttpAPI]
----

The important part of this simple API example is the `Callback` parameter that makes the API non-blocking and asynchronous.
