//
// ========================================================================
// 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
// ========================================================================
//

= I/O Architecture

The Jetty client libraries provide the basic components and APIs to implement a client application.

They build on the common xref:arch/io.adoc[Jetty I/O Architecture] and provide client specific concepts (such as establishing a connection to a server).

There are conceptually two layers that compose the Jetty client libraries:

. <<transport,The transport layer>>, that handles the low-level communication with the server, and deals with buffers, threads, etc.
. <<protocol,The protocol layer>>, that handles the high-level protocol by parsing the bytes read from the transport layer and by generating the bytes to write to the transport layer.

[[transport]]
== Transport Layer

The transport layer is the low-level layer that communicates with the server.

Protocols such as HTTP/1.1 and HTTP/2 are typically transported over TCP, while the newer HTTP/3 is transported over QUIC, which is itself transported over UDP.

However, there are other means of communication supported by the Jetty client libraries, in particular over <<unix-domain,Unix-Domain sockets>> (for inter-process communication), and over <<memory,memory>> (for intra-process communication).

The same high-level protocol can be carried by different low-level transports.
For example, the high-level HTTP/1.1 protocol can be transported over either TCP (the default), or QUIC, or Unix-Domain sockets, or memory, because all these low-level transport provide reliable and ordered communication between client and server.

Similarly, the high-level HTTP/3 protocol can be transported over either QUIC (the default) or memory.
It would be possible to transport HTTP/3 also over Unix-Domain sockets, but the current version of Java only supports Unix-Domain sockets for ``SocketChannel``s and not for ``DatagramChannel``s.

The Jetty client libraries use the common I/O design described in xref:arch/io.adoc[this section].

The common I/O components and concepts are used for all low-level transports.
The only partial exception is the <<memory,memory transport>>, which is not based on network components; as such it does not need a `SelectorManager`, but it exposes `EndPoint` so that high-level protocols have a common interface to interact with the low-level transport.

The client-side abstraction for the low-level transport is `org.eclipse.jetty.io.Transport`.

`Transport` represents how high-level protocols can be transported; there is `Transport.TCP_IP` that represents communication over TCP, but also `Transport.TCPUnix` for Unix-Domain sockets, `QuicTransport` for QUIC and `MemoryTransport` for memory.

Applications can specify the `Transport` to use for each request as described in xref:client/http.adoc#api-transport[this section].

When the `Transport` implementation uses the network, it delegates to `org.eclipse.jetty.io.ClientConnector`.

`ClientConnector` primarily wraps `org.eclipse.jetty.io.SelectorManager` to provide network functionalities, and aggregates other four components:

* a thread pool (in form of an `java.util.concurrent.Executor`)
* a scheduler (in form of `org.eclipse.jetty.util.thread.Scheduler`)
* a byte buffer pool (in form of `org.eclipse.jetty.io.ByteBufferPool`)
* a TLS factory (in form of `org.eclipse.jetty.util.ssl.SslContextFactory.Client`)

The `ClientConnector` is where you want to set those components after you have configured them.
If you don't explicitly set those components on the `ClientConnector`, then appropriate defaults will be chosen when the `ClientConnector` starts.

`ClientConnector` manages all network-related components, and therefore it is used for TCP, UDP, QUIC and <<unix-domain,Unix-Domain sockets>>.

The simplest example that creates and starts a `ClientConnector` is the following:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java[tags=simplest]
----

A more typical example:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java[tags=typical]
----

A more advanced example that customizes the `ClientConnector` by overriding some of its methods:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java[tags=advanced]
----

Since `ClientConnector` is the component that handles the low-level network transport, it is also the component where you want to configure the low-level network configuration.

The most common parameters are:

* `ClientConnector.selectors`: the number of ``java.nio.Selector``s components (defaults to `1`) that are present to handle the ``SocketChannel``s and ``DatagramChannel``s opened by the `ClientConnector`.
You typically want to increase the number of selectors only for those use cases where each selector should handle more than few hundreds _concurrent_ socket events.
For example, one selector typically runs well for `250` _concurrent_ socket events; as a rule of thumb, you can multiply that number by `10` to obtain the number of opened sockets a selector can handle (`2500`), based on the assumption that not all the `2500` sockets will be active _at the same time_.
* `ClientConnector.idleTimeout`: the duration of time after which `ClientConnector` closes a socket due to inactivity (defaults to `30` seconds).
This is an important parameter to configure, and you typically want the client idle timeout to be shorter than the server idle timeout, to avoid race conditions where the client attempts to use a socket just before the client-side idle timeout expires, but the server-side idle timeout has already expired and the is already closing the socket.
* `ClientConnector.connectBlocking`: whether the operation of connecting a socket to the server (i.e. `SocketChannel.connect(SocketAddress)`) must be a blocking or a non-blocking operation (defaults to `false`).
For `localhost` or same datacenter hosts you want to set this parameter to
`true` because DNS resolution will be immediate (and likely never fail).
For generic Internet hosts (e.g. when you are implementing a web spider) you want to set this parameter to `false`.
* `ClientConnector.connectTimeout`: the duration of time after which `ClientConnector` aborts a connection attempt to the server (defaults to `5` seconds).
This time includes the DNS lookup time _and_ the TCP connect time.

Please refer to the `ClientConnector` link:{javadoc-url}/org/eclipse/jetty/io/ClientConnector.html[javadocs] for the complete list of configurable parameters.

[[unix-domain]]
=== Unix-Domain Support

https://openjdk.java.net/jeps/380[JEP 380] introduced Unix-Domain sockets support in Java 16, on all operative systems, but only for ``SocketChannel``s (not for ``DatagramChannel``s).

`ClientConnector` handles Unix-Domain sockets exactly like it handles regular TCP sockets, so there is no additional configuration necessary -- Unix-Domain sockets are supported out-of-the-box.

Applications can specify the `Transport` to use for each request as described in xref:client/http.adoc#api-transport[this section].

[[memory]]
=== Memory Support

In addition to support communication between client and server via network or Unix-Domain, the Jetty client libraries also support communication between client and server via memory for intra-process communication.
This means that the client and server must be in the same JVM process.

This functionality is provided by `org.eclipse.jetty.server.MemoryTransport`, which does not delegate to `ClientConnector`, but instead delegates to the server-side `MemoryConnector` and its related classes.

Applications can specify the `Transport` to use for each request as described in xref:client/http.adoc#api-transport[this section].

[[protocol]]
== Protocol Layer

The protocol layer builds on top of the transport layer to generate the bytes to be written to the low-level transport and to parse the bytes read from the low-level transport.

Recall from xref:arch/io.adoc#connection[this section] that Jetty uses the `Connection` abstraction to produce and interpret the low-level transport bytes.

On the client side, a `ClientConnectionFactory` implementation is the component that creates `Connection` instances based on the protocol that the client wants to "speak" with the server.

Applications may use `ClientConnector.connect(SocketAddress, Map<String, Object>)` to establish a TCP connection to the server, and must provide `ClientConnector` with the following information in the context map:

* A `Transport` instance that specifies the low-level transport to use.
* A `ClientConnectionFactory` that creates `Connection` instances for the high-level protocol.
* A `Promise` that is notified when the connection creation succeeds or fails.

For example:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java[tags=connect]
----

When a `Connection` is created successfully, its `onOpen()` method is invoked, and then the promise is completed successfully.

It is now possible to write a super-simple `telnet` client that reads and writes string lines:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java[tags=telnet]
----

Note how a very basic "telnet" API that applications could use is implemented in the form of the `onLine(Consumer<String>)` for the non-blocking receiving side and `writeLine(String, Callback)` for the non-blocking sending side.
Note also how the `onFillable()` method implements some basic "parsing" by looking up the `\n` character in the buffer.

NOTE: The "telnet" client above looks like a super-simple HTTP client because HTTP/1.0 can be seen as a line-based protocol.
HTTP/1.0 was used just as an example, but we could have used any other line-based protocol such as https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol[SMTP], provided that the server was able to understand it.

This is very similar to what the Jetty client implementation does for real network protocols.
Real network protocols are of course more complicated and so is the implementation code that handles them, but the general ideas are similar.

The Jetty client implementation provides a number of `ClientConnectionFactory` implementations that can be composed to produce and interpret the network bytes.

For example, it is simple to modify the above example to use the TLS protocol so that you will be able to connect to the server on port `443`, typically reserved for the secure HTTP protocol.

The differences between the clear-text version and the TLS encrypted version are minimal:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/client/ClientConnectorDocs.java[tags=tlsTelnet]
----

The differences with the clear-text version are only:

* Change the port from `80` to `443`.
* Wrap the `ClientConnectionFactory` with `SslClientConnectionFactory`.
* Unwrap the `SslConnection` to access `TelnetConnection`.
