Draft 1 for week 1.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2019-07-31 15:33:08 +02:00
parent 6753b43d41
commit ecfa45c365
6 changed files with 375 additions and 13 deletions

View File

@ -1,14 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-documentation</artifactId>
<name>Jetty :: Documentation</name>
<packaging>pom</packaging>
<packaging>jar</packaging>
<properties>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
<html.common.directory>${project.build.directory}/html/common</html.common.directory>
@ -25,6 +27,15 @@
<html.directory>${project.build.directory}/current</html.directory>
<javadoc.version>${project.version}</javadoc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
@ -81,7 +92,6 @@
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>${asciidoctor.maven.plugin.version}</version>
<configuration>
<basedir>${basedir}/src/main/asciidoc</basedir>
<attributes>
<JDURL>http://www.eclipse.org/jetty/javadoc/${javadoc.version}</JDURL>
<JXURL>http://download.eclipse.org/jetty/stable-9/xref</JXURL>
@ -95,6 +105,7 @@
<MVNCENTRAL>http://central.maven.org/maven2</MVNCENTRAL>
<VERSION>${project.version}</VERSION>
<TIMESTAMP>${maven.build.timestamp}</TIMESTAMP>
<docbits>${basedir}/src/main/java</docbits>
</attributes>
</configuration>
<executions>
@ -142,7 +153,7 @@
</execution>
<execution>
<id>embedded-guide</id>
<phase>generate-resources</phase>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
@ -176,6 +187,7 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>alt-formats</id>
@ -210,7 +222,6 @@
<configuration>
<imagesdir />
<backend>docbook</backend>
<basedir>${basedir}/src/main/asciidoc</basedir>
<attributes>
<revnumber>${project.version}</revnumber>
<JDURL>http://www.eclipse.org/jetty/javadoc/${javadoc.version}</JDURL>
@ -284,4 +295,5 @@
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,182 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ========================================================================
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
[[client-concepts]]
=== Client Libraries Concepts
The Jetty client libraries implement a network client speaking different protocols
such as HTTP/1.1, HTTP/2, WebSocket and FastCGI.
It is possible to implement your own custom protocol on top of the Jetty client
libraries (TODO: perhaps add a section about this).
There are conceptually three layers that compose the Jetty client libraries, from
more abstract to more concrete:
. The API layer, that exposes semantic APIs to application so that they can write
code such as "GET me the resource at this URI"
. The protocol layer, where the API request is converted into the appropriate
protocol bytes, for example encrypted HTTP/2
. The infrastructure layer, that handles the low level I/O and deals with network,
buffer, threads, etc.
[[client-concepts-infrastructure]]
==== Client Libraries Infrastructure Layer
The Jetty client libraries reuse the same components also used on the server-side
to handle the low-level network, the thread pooling, the scheduler, etc.
The main client-side component is the
link:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[`ClientConnector`].
The `ClientConnector` primarily wraps the
link:{JDURL}/org/eclipse/jetty/io/SelectorManager.html[`SelectorManager`]
and aggregates other four components: the thread pool (in form of an `Executor`),
the `Scheduler`, the `ByteBufferPool` and the `SslContextFactory.Client`.
The `ClientConnector` is where you want to set those five components after you
have configured them.
If you don't explicitly set those five components on the `ClientConnector`, then
appropriate defaults will be chosen.
The simplest example that creates and start a `ClientConnector`:
[source,java,indent=0]
----
include::{docbits}/embedded/client/EmbeddedClientConnector.java[tags=simplest]
----
A more typical example:
[source,java,indent=0]
----
include::{docbits}/embedded/client/EmbeddedClientConnector.java[tags=typical]
----
A more advanced example that customizes the `ClientConnector` by overriding
factory methods:
[source,java,indent=0]
----
include::{docbits}/embedded/client/EmbeddedClientConnector.java[tags=advanced]
----
Since `ClientConnector` is the component that handles the low-level network, it
is also the component where you want to configure the parameters that control
how it should handle the low-level network.
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 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 idle
timeout expires, but the server is already closing it.
* `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:{JDURL}/org/eclipse/jetty/io/ClientConnector.html[javadocs]
for the complete list of configurable parameters.
Once the `ClientConnector` is configured and started, it can be used to connect
to the server via `ClientConnector.connect(SocketAddress, Map<String, Object>)`
which in turn will call `SocketChannel.connect(SocketAddress)`.
`ClientConnector` wraps the `SocketChannel` connected to the server with two
related components:
an link:{JDURL}/org/eclipse/jetty/io/EndPoint.html[`EndPoint`] and a
link:{JDURL}/org/eclipse/jetty/io/Connection.html[`Connection`].
`EndPoint` is the Jetty abstraction for a `SocketChannel`: you can read bytes
from an `EndPoint` via `EndPoint.fill(ByteBuffer)`, you can write bytes to an
`EndPoint` via `EndPoint.flush(ByteBuffer...)` and
`EndPoint.write(Callback, ByteBuffer...)`, you can close an `EndPoint` via
`EndPoint.close()`, etc.
There is primarily one implementation of `EndPoint`:
link:{JDURL}/org/eclipse/jetty/io/SocketChannelEndPoint.html[`SocketChannelEndPoint`].
`Connection` is the Jetty abstraction that is responsible to serialize objects
to bytes and then writing the serialized bytes to the `EndPoint`, as well as
deserializing bytes read from the `EndPoint` into objects.
For example, a HTTP/1.1 `Connection` implementation is responsible to serialize
a HTTP request object into its correspondent HTTP/1.1 request bytes, and to
deserialize HTTP/1.1 response bytes into a response object that can be used by
applications.
`Connection` is the abstraction that "speaks" a specific protocol such as
HTTP/1.1, or HTTP/2, or WebSocket: it is able to read incoming communication
as well as write outgoing communication in that protocol.
There are many implementation of `Connection`, typically one for each protocol
that the Jetty client libraries can speak.
``Connection``s can be nested, for example in case of encrypted communication
using the TLS protocol: the outermost TLS `Connection` performs the decryption
and encryption, and the innermost protocol `Connection` serializes the
decrypted bytes into objects and deserializes objects into bytes to be encrypted.
Certain protocols, such as WebSocket, start with the communication with the
server using one protocol (e.g. HTTP/1.1), but then change the communication
to use another protocol (e.g. WebSocket).
`EndPoint` supports changing the `Connection` object on-the-fly so that the
HTTP/1.1 `Connection` can be used during the initial communication and later
be replaced by a WebSocket `Connection`.
TODO: add a section on `UpgradeFrom` and `UpgradeTo`?
When establishing a TCP connection to a server, applications need to tell
`ClientConnector` how to create the `Connection` for that particular
TCP connection.
This is done via a
link:{JDURL}/org/eclipse/jetty/io/ClientConnectionFactory.html[`ClientConnectionFactory`].
that must be passed in the context `Map` as follows:
[source,java,indent=0]
----
include::{docbits}/embedded/client/EmbeddedClientConnector.java[tags=connect]
----
TODO: expand on what is the API to use, what parameters the context Map must
have, and basically how we can write a generic network client with it.
[[client-concepts-protocol]]
==== Client Libraries Protocol Layer
The protocol layer builds on top of the infrastructure layer to generate the
bytes to be written to the network and to parse the bytes received from the
network.

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ========================================================================
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
[[client]]
== Jetty Client Libraries
The Eclipse Jetty Project provides not only server-side libraries so that you
can embed a server in your code, but it also provides client-side libraries
that allow you to embed a client - for example a HTTP client invoking a third
party HTTP service - in your application.
The client libraries are designed to be non-blocking and offer both synchronous
and asynchronous APIs and come with a large number of configuration options.
There are primarily two client libraries:
* link:#client-http[The HTTP client library]
* link:#client-websocket[The WebSocket client library]
include::client-concepts.adoc[]

View File

@ -59,6 +59,7 @@ endif::[]
// suppress automatic id generation
:sectids!:
include::client/client.adoc[]
include::embedding/chapter.adoc[]
include::maven/chapter.adoc[]
include::clients/http/chapter.adoc[]

View File

@ -0,0 +1,132 @@
package embedded.client;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
import static java.lang.System.Logger.Level.INFO;
public class EmbeddedClientConnector
{
public void simplest() throws Exception
{
// tag::simplest[]
ClientConnector clientConnector = new ClientConnector();
clientConnector.start();
// end::simplest[]
}
public void typical() throws Exception
{
// tag::typical[]
// Create and configure the SslContextFactory.
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
sslContextFactory.addExcludeProtocols("TLSv1", "TLSv1.1");
// Create and configure the thread pool.
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName("client");
// Create and configure the ClientConnector.
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSslContextFactory(sslContextFactory);
clientConnector.setExecutor(threadPool);
clientConnector.start();
// end::typical[]
}
public void advanced() throws Exception
{
// tag::advanced[]
class CustomClientConnector extends ClientConnector
{
@Override
protected SelectorManager newSelectorManager()
{
return new ClientSelectorManager(getExecutor(), getScheduler(), getSelectors())
{
@Override
protected void endPointOpened(EndPoint endpoint)
{
System.getLogger("endpoint").log(INFO, "opened %s", endpoint);
}
@Override
protected void endPointClosed(EndPoint endpoint)
{
System.getLogger("endpoint").log(INFO, "closed %s", endpoint);
}
};
}
}
// Create and configure the thread pool.
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName("client");
// Create and configure the scheduler.
Scheduler scheduler = new ScheduledExecutorScheduler("scheduler-client", false);
// Create and configure the custom ClientConnector.
CustomClientConnector clientConnector = new CustomClientConnector();
clientConnector.setExecutor(threadPool);
clientConnector.setScheduler(scheduler);
clientConnector.start();
// end::advanced[]
}
public void connect() throws Exception
{
class CustomHTTPConnection extends AbstractConnection
{
public CustomHTTPConnection(EndPoint endPoint, Executor executor)
{
super(endPoint, executor);
}
@Override
public void onOpen()
{
super.onOpen();
}
@Override
public void onFillable()
{
}
}
ClientConnector clientConnector = new ClientConnector();
clientConnector.start();
String host = "wikipedia.org";
int port = 80;
SocketAddress address = new InetSocketAddress(host, port);
ClientConnectionFactory connectionFactory = (endPoint, context) ->
{
System.getLogger("connection").log(INFO, "Creating connection for {0}", endPoint);
return new CustomHTTPConnection(endPoint, clientConnector.getExecutor());
};
Map<String, Object> context = new HashMap<>();
context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, connectionFactory);
clientConnector.connect(address, context);
}
public static void main(String[] args) throws Exception
{
new EmbeddedClientConnector().connect();
}
}